diff options
Diffstat (limited to 'lib')
274 files changed, 27153 insertions, 19444 deletions
diff --git a/lib/common_test/doc/src/notes.xml b/lib/common_test/doc/src/notes.xml index a64818da7b..c454608bbe 100644 --- a/lib/common_test/doc/src/notes.xml +++ b/lib/common_test/doc/src/notes.xml @@ -62,6 +62,37 @@ </section> +<section><title>Common_Test 1.17.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + If a ct hook is installed in the <c>suite/0</c> function + in a test suite, then the hook's <c>terminate/1</c> + function would be called several times without it's + <c>init/2</c> function being called first. This is now + corrected.</p> + <p> + Own Id: OTP-15863 Aux Id: ERIERL-370 </p> + </item> + <item> + <p> + If <c>init_per_testcase</c> fails, the test itself is + skipped. According to the documentation, it should be + possible to change the result to failed in a hook + function. The only available hook function in this case + is <c>post_init_per_testcase</c>, but changing the return + value there did not affect the test case result. This is + now corrected.</p> + <p> + Own Id: OTP-15869 Aux Id: ERIERL-350 </p> + </item> + </list> + </section> + +</section> + <section><title>Common_Test 1.17.2</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -239,6 +270,37 @@ </section> +<section><title>Common_Test 1.15.4.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + If a ct hook is installed in the <c>suite/0</c> function + in a test suite, then the hook's <c>terminate/1</c> + function would be called several times without it's + <c>init/2</c> function being called first. This is now + corrected.</p> + <p> + Own Id: OTP-15863 Aux Id: ERIERL-370 </p> + </item> + <item> + <p> + If <c>init_per_testcase</c> fails, the test itself is + skipped. According to the documentation, it should be + possible to change the result to failed in a hook + function. The only available hook function in this case + is <c>post_init_per_testcase</c>, but changing the return + value there did not affect the test case result. This is + now corrected.</p> + <p> + Own Id: OTP-15869 Aux Id: ERIERL-350 </p> + </item> + </list> + </section> + +</section> + <section><title>Common_Test 1.15.4.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/common_test/src/ct_hooks.erl b/lib/common_test/src/ct_hooks.erl index 97c349578f..94551d6815 100644 --- a/lib/common_test/src/ct_hooks.erl +++ b/lib/common_test/src/ct_hooks.erl @@ -363,7 +363,16 @@ terminate_if_scope_ends(HookId, Function0, Hooks) -> Function = strip_config(Function0), case lists:keyfind(HookId, #ct_hook_config.id, Hooks) of #ct_hook_config{ id = HookId, scope = Function} = Hook -> - terminate([Hook]), + case Function of + [AllOrGroup,_] when AllOrGroup=:=post_all; + AllOrGroup=:=post_groups -> + %% The scope only contains one function (post_all + %% or post_groups), and init has not been called, + %% so skip terminate as well. + ok; + _ -> + terminate([Hook]) + end, lists:keydelete(HookId, #ct_hook_config.id, Hooks); _ -> Hooks diff --git a/lib/common_test/src/ct_release_test.erl b/lib/common_test/src/ct_release_test.erl index ac3dcab7c9..839fb300c7 100644 --- a/lib/common_test/src/ct_release_test.erl +++ b/lib/common_test/src/ct_release_test.erl @@ -475,7 +475,7 @@ fetch_all_apps(Node) -> A = list_to_atom(filename:basename(filename:rootname(F))), _ = rpc:call(Node,application,load,[A]), case rpc:call(Node,application,get_key,[A,vsn]) of - {ok,V} -> [{A,V}]; + {ok,V} -> [{A,V,rpc:call(Node,code,lib_dir,[A])}]; _ -> [] end end, @@ -517,7 +517,7 @@ upgrade(Apps,Level,Callback,CreateDir,InstallDir,Config) -> target_system(Apps,CreateDir,InstallDir,{FromVsn,_,AllAppsVsns,Path}) -> RelName0 = "otp-"++FromVsn, - AppsVsns = [{A,V} || {A,V} <- AllAppsVsns, lists:member(A,Apps)], + AppsVsns = [{A,V,D} || {A,V,D} <- AllAppsVsns, lists:member(A,Apps)], {RelName,ErtsVsn} = create_relfile(AppsVsns,CreateDir,RelName0,FromVsn), %% Create .script and .boot @@ -636,8 +636,7 @@ do_upgrade({Cb,InitState},FromVsn,FromAppsVsns,ToRel,ToAppsVsns,InstallDir) -> {ok,Node} = start_node(Start,FromVsn,FromAppsVsns), ct:log("Node started: ~p",[Node]), - CtData = #ct_data{from = [{A,V,code:lib_dir(A)} || {A,V} <- FromAppsVsns], - to=[{A,V,code:lib_dir(A)} || {A,V} <- ToAppsVsns]}, + CtData = #ct_data{from = FromAppsVsns,to=ToAppsVsns}, State1 = do_callback(Node,Cb,upgrade_init,[CtData,InitState]), [{"OTP upgrade test",FromVsn,_,permanent}] = @@ -724,14 +723,14 @@ previous_major(Rel) -> integer_to_list(list_to_integer(Rel)-1). create_relfile(AppsVsns,CreateDir,RelName0,RelVsn) -> - UpgradeAppsVsns = [{A,V,restart_type(A)} || {A,V} <- AppsVsns], + UpgradeAppsVsns = [{A,V,restart_type(A)} || {A,V,_D} <- AppsVsns], CoreAppVsns0 = get_vsns([kernel,stdlib,sasl]), CoreAppVsns = - [{A,V,restart_type(A)} || {A,V} <- CoreAppVsns0, + [{A,V,restart_type(A)} || {A,V,_D} <- CoreAppVsns0, false == lists:keymember(A,1,AppsVsns)], - Apps = [App || {App,_} <- AppsVsns], + Apps = [App || {App,_,_} <- AppsVsns], StartDepsVsns = get_start_deps(Apps,CoreAppVsns), StartApps = [StartApp || {StartApp,_,_} <- StartDepsVsns] ++ Apps, @@ -744,7 +743,7 @@ create_relfile(AppsVsns,CreateDir,RelName0,RelVsn) -> %% processes of these applications will not be running. TestToolAppsVsns0 = get_vsns([common_test]), TestToolAppsVsns = - [{A,V,none} || {A,V} <- TestToolAppsVsns0, + [{A,V,none} || {A,V,_D} <- TestToolAppsVsns0, false == lists:keymember(A,1,AllAppsVsns0)], AllAppsVsns1 = AllAppsVsns0 ++ TestToolAppsVsns, @@ -766,7 +765,7 @@ get_vsns(Apps) -> [begin _ = application:load(A), {ok,V} = application:get_key(A,vsn), - {A,V} + {A,V,code:lib_dir(A)} end || A <- Apps]. get_start_deps([App|Apps],Acc) -> @@ -880,8 +879,9 @@ start_node(Start,ExpVsn,ExpAppsVsns) -> erlang:port_close(Port), wait_node_up(permanent,ExpVsn,ExpAppsVsns). -wait_node_up(ExpStatus,ExpVsn,ExpAppsVsns) -> +wait_node_up(ExpStatus,ExpVsn,ExpAppsVsns0) -> Node = node_name(?testnode), + ExpAppsVsns = [{A,V} || {A,V,_D} <- ExpAppsVsns0], wait_node_up(Node,ExpStatus,ExpVsn,lists:keysort(1,ExpAppsVsns),60). wait_node_up(Node,ExpStatus,ExpVsn,ExpAppsVsns,0) -> @@ -893,7 +893,7 @@ wait_node_up(Node,ExpStatus,ExpVsn,ExpAppsVsns,N) -> rpc:call(Node, application, which_applications, [])} of {[{_,ExpVsn,_,_}],Apps} when is_list(Apps) -> case [{A,V} || {A,_,V} <- lists:keysort(1,Apps), - lists:keymember(A,1,ExpAppsVsns)] of + lists:keymember(A,1,ExpAppsVsns)] of ExpAppsVsns -> {ok,Node}; _ -> diff --git a/lib/common_test/src/test_server.erl b/lib/common_test/src/test_server.erl index 756cd4d692..588396f101 100644 --- a/lib/common_test/src/test_server.erl +++ b/lib/common_test/src/test_server.erl @@ -1364,23 +1364,29 @@ do_end_tc_call(Mod, IPTC={init_per_testcase,Func}, Res, Return) -> {NOk,_} when NOk == auto_skip; NOk == fail; NOk == skip ; NOk == skipped -> {_,Args} = Res, - IPTCEndRes = + {NewConfig,IPTCEndRes} = case do_end_tc_call1(Mod, IPTC, Res, Return) of IPTCEndConfig when is_list(IPTCEndConfig) -> - IPTCEndConfig; + {IPTCEndConfig,IPTCEndConfig}; + {failed,RetReason} when Return=:={fail,RetReason} -> + %% Fail reason not changed by framework or hook + {Args,Return}; + {SF,_} = IPTCEndResult when SF=:=skip; SF=:=skipped; + SF=:=fail; SF=:=failed -> + {Args,IPTCEndResult}; _ -> - Args + {Args,Return} end, EPTCInitRes = case do_init_tc_call(Mod,{end_per_testcase_not_run,Func}, - IPTCEndRes,Return) of + NewConfig,IPTCEndRes) of {ok,EPTCInitConfig} when is_list(EPTCInitConfig) -> - {Return,EPTCInitConfig}; + {IPTCEndRes,EPTCInitConfig}; _ -> - {Return,IPTCEndRes} + {IPTCEndRes,NewConfig} end, do_end_tc_call1(Mod, {end_per_testcase_not_run,Func}, - EPTCInitRes, Return); + EPTCInitRes, IPTCEndRes); _Ok -> do_end_tc_call1(Mod, IPTC, Res, Return) end; diff --git a/lib/common_test/test/ct_hooks_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE.erl index 340b8f3d52..b87464f5e4 100644 --- a/lib/common_test/test/ct_hooks_SUITE.erl +++ b/lib/common_test/test/ct_hooks_SUITE.erl @@ -86,7 +86,7 @@ all(suite) -> scope_suite_state_cth, fail_pre_suite_cth, double_fail_pre_suite_cth, fail_post_suite_cth, skip_pre_suite_cth, skip_pre_end_cth, - skip_pre_init_tc_cth, + skip_pre_init_tc_cth, fail_post_init_tc_cth, skip_post_suite_cth, recover_post_suite_cth, update_config_cth, state_update_cth, update_result_cth, options_cth, same_id_cth, fail_n_skip_with_minimal_cth, prio_cth, no_config, @@ -206,6 +206,10 @@ skip_pre_init_tc_cth(Config) -> do_test(skip_pre_init_tc_cth, "ct_cth_empty_SUITE.erl", [skip_pre_init_tc_cth],Config). +fail_post_init_tc_cth(Config) -> + do_test(fail_post_init_tc_cth, "ct_fail_init_tc_SUITE.erl", + [fail_post_init_tc_cth],Config). + recover_post_suite_cth(Config) when is_list(Config) -> do_test(recover_post_suite_cth, "ct_cth_fail_per_suite_SUITE.erl", [recover_post_suite_cth],Config). @@ -671,9 +675,15 @@ test_events(scope_suite_cth) -> {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, %% check that post_groups and post_all comes before init when hook %% is installed in suite/0 + %% And there should be no terminate after these, since init is + %% not yet called. {?eh,cth,{'_',post_groups,['_',[]]}}, - {?eh,cth,{'_',post_all,['_','_',[]]}}, - {?eh,tc_start,{ct_scope_suite_cth_SUITE,init_per_suite}}, + {negative, + {?eh,cth,{'_',terminate,['_']}}, + {?eh,cth,{'_',post_all,['_','_',[]]}}}, + {negative, + {?eh,cth,{'_',terminate,['_']}}, + {?eh,tc_start,{ct_scope_suite_cth_SUITE,init_per_suite}}}, {?eh,cth,{'_',id,[[]]}}, {?eh,cth,{'_',init,['_',[]]}}, {?eh,cth,{'_',pre_init_per_suite,[ct_scope_suite_cth_SUITE,'$proplist',[]]}}, @@ -1036,6 +1046,44 @@ test_events(skip_pre_init_tc_cth) -> {?eh,stop_logging,[]} ]; +test_events(fail_post_init_tc_cth) -> + [ + {?eh,start_logging,{'DEF','RUNDIR'}}, + {?eh,test_start,{'DEF',{'START_TIME','LOGDIR'}}}, + {?eh,cth,{empty_cth,init,['_',[]]}}, + {?eh,start_info,{1,1,1}}, + {?eh,tc_start,{ct_fail_init_tc_SUITE,init_per_suite}}, + {?eh,cth,{empty_cth,pre_init_per_suite,[ct_fail_init_tc_SUITE,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_init_per_suite, + [ct_fail_init_tc_SUITE,'$proplist','$proplist',[]]}}, + {?eh,tc_done,{ct_fail_init_tc_SUITE,init_per_suite,ok}}, + {?eh,tc_start,{ct_fail_init_tc_SUITE,test_case}}, + {?eh,cth,{empty_cth,pre_init_per_testcase, + [ct_fail_init_tc_SUITE,test_case,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_init_per_testcase, + [ct_fail_init_tc_SUITE,test_case,'$proplist', + {skip, + {failed, + {ct_fail_init_tc_SUITE,init_per_testcase, + {{test_case_failed,"Failed in init_per_testcase"},'_'}}}}, + []]}}, + {?eh,tc_done,{ct_fail_init_tc_SUITE,test_case, + {failed,"Changed skip to fail in post_init_per_testcase"}}}, + {?eh,cth,{empty_cth,on_tc_fail, + [ct_fail_init_tc_SUITE,test_case, + "Changed skip to fail in post_init_per_testcase", + []]}}, + {?eh,test_stats,{0,1,{0,0}}}, + {?eh,tc_start,{ct_fail_init_tc_SUITE,end_per_suite}}, + {?eh,cth,{empty_cth,pre_end_per_suite,[ct_fail_init_tc_SUITE,'$proplist',[]]}}, + {?eh,cth,{empty_cth,post_end_per_suite, + [ct_fail_init_tc_SUITE,'$proplist',ok,[]]}}, + {?eh,tc_done,{ct_fail_init_tc_SUITE,end_per_suite,ok}}, + {?eh,test_done,{'DEF','STOP_TIME'}}, + {?eh,cth,{empty_cth,terminate,[[]]}}, + {?eh,stop_logging,[]} + ]; + test_events(recover_post_suite_cth) -> Suite = ct_cth_fail_per_suite_SUITE, [ diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_fail_init_tc_SUITE.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_fail_init_tc_SUITE.erl new file mode 100644 index 0000000000..96ddfc5782 --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/ct_fail_init_tc_SUITE.erl @@ -0,0 +1,49 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010-2016. 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(ct_fail_init_tc_SUITE). + +-suite_defaults([{timetrap, {minutes, 10}}]). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include("ct.hrl"). + +%% Test server callback functions +init_per_suite(Config) -> + Config. + +end_per_suite(_Config) -> + ok. + +init_per_testcase(TestCase, _Config) -> + ct:fail("Failed in init_per_testcase"). + +end_per_testcase(_TestCase, _Config) -> + ok. + +all() -> + [test_case]. + +%% Test cases starts here. +test_case(Config) when is_list(Config) -> + ok. + diff --git a/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_init_tc_cth.erl b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_init_tc_cth.erl new file mode 100644 index 0000000000..ca9f05c40f --- /dev/null +++ b/lib/common_test/test/ct_hooks_SUITE_data/cth/tests/fail_post_init_tc_cth.erl @@ -0,0 +1,81 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2010-2017. 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(fail_post_init_tc_cth). + + +-include_lib("common_test/src/ct_util.hrl"). +-include_lib("common_test/include/ct_event.hrl"). + + +%% CT Hooks +-compile(export_all). + +init(Id, Opts) -> + empty_cth:init(Id, Opts). + +pre_init_per_suite(Suite, Config, State) -> + empty_cth:pre_init_per_suite(Suite,Config,State). + +post_init_per_suite(Suite,Config,Return,State) -> + empty_cth:post_init_per_suite(Suite,Config,Return,State). + +pre_end_per_suite(Suite,Config,State) -> + empty_cth:pre_end_per_suite(Suite,Config,State). + +post_end_per_suite(Suite,Config,Return,State) -> + empty_cth:post_end_per_suite(Suite,Config,Return,State). + +pre_init_per_group(Suite,Group,Config,State) -> + empty_cth:pre_init_per_group(Suite,Group,Config,State). + +post_init_per_group(Suite,Group,Config,Return,State) -> + empty_cth:post_init_per_group(Suite,Group,Config,Return,State). + +pre_end_per_group(Suite,Group,Config,State) -> + empty_cth:pre_end_per_group(Suite,Group,Config,State). + +post_end_per_group(Suite,Group,Config,Return,State) -> + empty_cth:post_end_per_group(Suite,Group,Config,Return,State). + +pre_init_per_testcase(Suite,TC,Config,State) -> + empty_cth:pre_init_per_testcase(Suite,TC,Config,State). + +post_init_per_testcase(Suite,TC,Config,{skip,_}=Return,State) -> + empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State), + {{fail,"Changed skip to fail in post_init_per_testcase"},State}; +post_init_per_testcase(Suite,TC,Config,Return,State) -> + empty_cth:post_init_per_testcase(Suite,TC,Config,Return,State). + +pre_end_per_testcase(Suite,TC,Config,State) -> + empty_cth:pre_end_per_testcase(Suite,TC,Config,State). + +post_end_per_testcase(Suite,TC,Config,Return,State) -> + empty_cth:post_end_per_testcase(Suite,TC,Config,Return,State). + +on_tc_fail(Suite,TC, Reason, State) -> + empty_cth:on_tc_fail(Suite,TC,Reason,State). + +on_tc_skip(Suite,TC, Reason, State) -> + empty_cth:on_tc_skip(Suite,TC,Reason,State). + +terminate(State) -> + empty_cth:terminate(State). diff --git a/lib/common_test/test/ct_release_test_SUITE_data/release_test_SUITE.erl b/lib/common_test/test/ct_release_test_SUITE_data/release_test_SUITE.erl index 7f0ba65791..fe69ad0748 100644 --- a/lib/common_test/test/ct_release_test_SUITE_data/release_test_SUITE.erl +++ b/lib/common_test/test/ct_release_test_SUITE_data/release_test_SUITE.erl @@ -47,7 +47,9 @@ end_per_suite(_Config) -> init_per_testcase(major_fail_no_init, Config) -> Config; init_per_testcase(_Case, Config) -> - ct_release_test:init(Config). + Config1 = ct_release_test:init(Config), + ct:log("ct_release_test:init/1 returned:~n~p",[Config1]), + Config1. end_per_testcase(_Case, Config) -> ct_release_test:cleanup(Config). diff --git a/lib/compiler/src/beam_ssa.erl b/lib/compiler/src/beam_ssa.erl index a9977b0b1d..6492d1e1bf 100644 --- a/lib/compiler/src/beam_ssa.erl +++ b/lib/compiler/src/beam_ssa.erl @@ -96,7 +96,8 @@ %% To avoid the collapsing, change the value of SET_LIMIT to 50 in the %% file erl_types.erl in the hipe application. --type prim_op() :: 'bs_add' | 'bs_extract' | 'bs_init' | 'bs_init_writable' | +-type prim_op() :: 'bs_add' | 'bs_extract' | 'bs_get_tail' | + 'bs_init' | 'bs_init_writable' | 'bs_match' | 'bs_put' | 'bs_start_match' | 'bs_test_tail' | 'bs_utf16_size' | 'bs_utf8_size' | 'build_stacktrace' | 'call' | 'catch_end' | @@ -117,11 +118,12 @@ '+' | '-' | '*' | '/'. %% Primops only used internally during code generation. --type cg_prim_op() :: 'bs_get' | 'bs_match_string' | 'bs_restore' | 'bs_skip' | +-type cg_prim_op() :: 'bs_get' | 'bs_get_position' | 'bs_match_string' | + 'bs_restore' | 'bs_save' | 'bs_set_position' | 'bs_skip' | 'copy' | 'put_tuple_arity' | 'put_tuple_element' | - 'set_tuple_element'. + 'put_tuple_elements' | 'set_tuple_element'. --import(lists, [foldl/3,keyfind/3,mapfoldl/3,member/2,reverse/1]). +-import(lists, [foldl/3,keyfind/3,mapfoldl/3,member/2,reverse/1,umerge/1]). -spec add_anno(Key, Value, Construct) -> Construct when Key :: atom(), @@ -649,12 +651,18 @@ is_commutative('=/=') -> true; is_commutative('/=') -> true; is_commutative(_) -> false. -def_used_1([#b_blk{is=Is,last=Last}|Bs], Preds, Def0, Used0) -> - {Def,Used1} = def_used_is(Is, Preds, Def0, Used0), - Used = ordsets:union(used(Last), Used1), - def_used_1(Bs, Preds, Def, Used); -def_used_1([], _Preds, Def, Used) -> - {ordsets:from_list(Def),Used}. +def_used_1([#b_blk{is=Is,last=Last}|Bs], Preds, Def0, UsedAcc) -> + {Def,Used} = def_used_is(Is, Preds, Def0, used(Last)), + case Used of + [] -> + def_used_1(Bs, Preds, Def, UsedAcc); + [_|_] -> + def_used_1(Bs, Preds, Def, [Used|UsedAcc]) + end; +def_used_1([], _Preds, Def0, UsedAcc) -> + Def = ordsets:from_list(Def0), + Used = umerge(UsedAcc), + {Def,Used}. def_used_is([#b_set{op=phi,dst=Dst,args=Args}|Is], Preds, Def0, Used0) -> diff --git a/lib/compiler/src/cerl.erl b/lib/compiler/src/cerl.erl index 62cd5b5120..bc28f58712 100644 --- a/lib/compiler/src/cerl.erl +++ b/lib/compiler/src/cerl.erl @@ -263,7 +263,7 @@ %% @see subtrees/1 %% @see meta/1 --type ctype() :: 'alias' | 'apply' | 'binary' | 'bitrst' | 'call' | 'case' +-type ctype() :: 'alias' | 'apply' | 'binary' | 'bitstr' | 'call' | 'case' | 'catch' | 'clause' | 'cons' | 'fun' | 'let' | 'letrec' | 'literal' | 'map' | 'map_pair' | 'module' | 'primop' | 'receive' | 'seq' | 'try' | 'tuple' | 'values' | 'var'. diff --git a/lib/compiler/src/v3_core.erl b/lib/compiler/src/v3_core.erl index 3699c9d22e..007a0247f4 100644 --- a/lib/compiler/src/v3_core.erl +++ b/lib/compiler/src/v3_core.erl @@ -1811,7 +1811,8 @@ force_safe(Ce, St0) -> is_safe(#c_cons{}) -> true; is_safe(#c_tuple{}) -> true; -is_safe(#c_var{}) -> true; +is_safe(#c_var{name={_,_}}) -> false; %Fun. Not safe. +is_safe(#c_var{name=_}) -> true; %Ordinary variable. is_safe(#c_literal{}) -> true; is_safe(_) -> false. diff --git a/lib/compiler/test/fun_SUITE.erl b/lib/compiler/test/fun_SUITE.erl index 1df0a05275..7fc6195e31 100644 --- a/lib/compiler/test/fun_SUITE.erl +++ b/lib/compiler/test/fun_SUITE.erl @@ -22,7 +22,8 @@ -export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1, init_per_group/2,end_per_group/2, test1/1,overwritten_fun/1,otp_7202/1,bif_fun/1, - external/1,eep37/1,eep37_dup/1,badarity/1,badfun/1]). + external/1,eep37/1,eep37_dup/1,badarity/1,badfun/1, + duplicated_fun/1]). %% Internal exports. -export([call_me/1,dup1/0,dup2/0]). @@ -37,7 +38,7 @@ all() -> groups() -> [{p,[parallel], [test1,overwritten_fun,otp_7202,bif_fun,external,eep37, - eep37_dup,badarity,badfun]}]. + eep37_dup,badarity,badfun,duplicated_fun]}]. init_per_suite(Config) -> test_lib:recompile(?MODULE), @@ -261,5 +262,20 @@ badfun(_Config) -> expect_badfun(Term, Exit) -> {'EXIT',{{badfun,Term},_}} = Exit. +duplicated_fun(_Config) -> + try + %% The following code used to crash the compiler before + %% v3_core:is_safe/1 was corrected to consider fun variables + %% unsafe. + id([print_result_paths_fun = fun duplicated_fun_helper/1]), + ct:error(should_fail) + catch + error:{badmatch,F} when is_function(F, 1) -> + ok + end. + +duplicated_fun_helper(_) -> + ok. + id(I) -> I. diff --git a/lib/crypto/c_src/Makefile.in b/lib/crypto/c_src/Makefile.in index b6a65d7488..f922c3fb9b 100644 --- a/lib/crypto/c_src/Makefile.in +++ b/lib/crypto/c_src/Makefile.in @@ -92,16 +92,15 @@ CRYPTO_OBJS = $(OBJDIR)/crypto$(TYPEMARKER).o \ $(OBJDIR)/hash$(TYPEMARKER).o \ $(OBJDIR)/hmac$(TYPEMARKER).o \ $(OBJDIR)/info$(TYPEMARKER).o \ + $(OBJDIR)/mac$(TYPEMARKER).o \ $(OBJDIR)/math$(TYPEMARKER).o \ $(OBJDIR)/pkey$(TYPEMARKER).o \ - $(OBJDIR)/poly1305$(TYPEMARKER).o \ $(OBJDIR)/rand$(TYPEMARKER).o \ $(OBJDIR)/rsa$(TYPEMARKER).o \ $(OBJDIR)/srp$(TYPEMARKER).o CALLBACK_OBJS = $(OBJDIR)/crypto_callback$(TYPEMARKER).o NIF_MAKEFILE = $(PRIVDIR)/Makefile -CRYPTO_STATIC_OBJS = $(OBJDIR)/crypto_static$(TYPEMARKER).o\ - $(OBJDIR)/crypto_callback_static$(TYPEMARKER).o +CRYPTO_STATIC_OBJS = $(patsubst $(OBJDIR)/%$(TYPEMARKER).o,$(OBJDIR)/%_static$(TYPEMARKER).o,$(CRYPTO_OBJS) $(CALLBACK_OBJS)) NIF_ARCHIVE = $(LIBDIR)/crypto$(TYPEMARKER).a diff --git a/lib/crypto/c_src/algorithms.c b/lib/crypto/c_src/algorithms.c index 75cddeb1e9..53b8b7eaa9 100644 --- a/lib/crypto/c_src/algorithms.c +++ b/lib/crypto/c_src/algorithms.c @@ -20,13 +20,12 @@ #include "algorithms.h" #include "cipher.h" +#include "mac.h" static unsigned int algo_hash_cnt, algo_hash_fips_cnt; static ERL_NIF_TERM algo_hash[14]; /* increase when extending the list */ static unsigned int algo_pubkey_cnt, algo_pubkey_fips_cnt; static ERL_NIF_TERM algo_pubkey[12]; /* increase when extending the list */ -static unsigned int algo_mac_cnt, algo_mac_fips_cnt; -static ERL_NIF_TERM algo_mac[3]; /* increase when extending the list */ static unsigned int algo_curve_cnt, algo_curve_fips_cnt; static ERL_NIF_TERM algo_curve[89]; /* increase when extending the list */ static unsigned int algo_rsa_opts_cnt, algo_rsa_opts_fips_cnt; @@ -101,19 +100,6 @@ void init_algorithms_types(ErlNifEnv* env) #endif algo_pubkey[algo_pubkey_cnt++] = enif_make_atom(env, "srp"); - - // Validated algorithms first - algo_mac_cnt = 0; - algo_mac[algo_mac_cnt++] = enif_make_atom(env,"hmac"); -#ifdef HAVE_CMAC - algo_mac[algo_mac_cnt++] = enif_make_atom(env,"cmac"); -#endif -#ifdef HAVE_POLY1305 - algo_mac[algo_mac_cnt++] = enif_make_atom(env,"poly1305"); -#endif - // Non-validated algorithms follow - algo_mac_fips_cnt = algo_mac_cnt; - // Validated algorithms first algo_curve_cnt = 0; #if defined(HAVE_EC) @@ -250,7 +236,6 @@ void init_algorithms_types(ErlNifEnv* env) // Check that the max number of algos is updated ASSERT(algo_hash_cnt <= sizeof(algo_hash)/sizeof(ERL_NIF_TERM)); ASSERT(algo_pubkey_cnt <= sizeof(algo_pubkey)/sizeof(ERL_NIF_TERM)); - ASSERT(algo_mac_cnt <= sizeof(algo_mac)/sizeof(ERL_NIF_TERM)); ASSERT(algo_curve_cnt <= sizeof(algo_curve)/sizeof(ERL_NIF_TERM)); ASSERT(algo_rsa_opts_cnt <= sizeof(algo_rsa_opts)/sizeof(ERL_NIF_TERM)); } @@ -284,18 +269,12 @@ ERL_NIF_TERM cipher_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv return cipher_types_as_list(env); /* Exclude old api ciphers */ } + ERL_NIF_TERM mac_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { - unsigned int cnt = -#ifdef FIPS_SUPPORT - FIPS_mode() ? algo_mac_fips_cnt : -#endif - algo_mac_cnt; - - return enif_make_list_from_array(env, algo_mac, cnt); + return mac_types_as_list(env); } - ERL_NIF_TERM curve_algorithms(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) { unsigned int cnt = diff --git a/lib/crypto/c_src/api_ng.c b/lib/crypto/c_src/api_ng.c index 3408ba1b88..941e03cc98 100644 --- a/lib/crypto/c_src/api_ng.c +++ b/lib/crypto/c_src/api_ng.c @@ -100,7 +100,7 @@ static int get_init_args(ErlNifEnv* env, } - if (FORBIDDEN_IN_FIPS(*cipherp)) + if (CIPHER_FORBIDDEN_IN_FIPS(*cipherp)) { *return_term = EXCP_NOTSUP(env, "Forbidden in FIPS"); goto err; @@ -334,12 +334,11 @@ ERL_NIF_TERM ng_crypto_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM arg if ((ctx_res = enif_alloc_resource(evp_cipher_ctx_rtype, sizeof(struct evp_cipher_ctx))) == NULL) return EXCP_ERROR(env, "Can't allocate resource"); - if (!get_init_args(env, ctx_res, argv[0], argv[1], argv[2], argv[argc-1], + if (get_init_args(env, ctx_res, argv[0], argv[1], argv[2], argv[argc-1], &cipherp, &ret)) - /* Error msg in &ret */ - goto ret; + ret = enif_make_resource(env, ctx_res); + /* else error msg in ret */ - ret = enif_make_resource(env, ctx_res); if(ctx_res) enif_release_resource(ctx_res); } else if (enif_get_resource(env, argv[0], (ErlNifResourceType*)evp_cipher_ctx_rtype, (void**)&ctx_res)) { diff --git a/lib/crypto/c_src/atoms.c b/lib/crypto/c_src/atoms.c index 059c14690f..bbeb329fa2 100644 --- a/lib/crypto/c_src/atoms.c +++ b/lib/crypto/c_src/atoms.c @@ -30,6 +30,10 @@ ERL_NIF_TERM atom_rsa_no_padding; ERL_NIF_TERM atom_signature_md; ERL_NIF_TERM atom_undefined; +ERL_NIF_TERM atom_hmac; +ERL_NIF_TERM atom_cmac; +ERL_NIF_TERM atom_poly1305; + ERL_NIF_TERM atom_ok; ERL_NIF_TERM atom_none; ERL_NIF_TERM atom_notsup; @@ -155,6 +159,11 @@ int init_atoms(ErlNifEnv *env, const ERL_NIF_TERM fips_mode, const ERL_NIF_TERM atom_rsa_no_padding = enif_make_atom(env,"rsa_no_padding"); atom_signature_md = enif_make_atom(env,"signature_md"); atom_undefined = enif_make_atom(env,"undefined"); + + atom_hmac = enif_make_atom(env,"hmac"); + atom_cmac = enif_make_atom(env,"cmac"); + atom_poly1305 = enif_make_atom(env,"poly1305"); + atom_ok = enif_make_atom(env,"ok"); atom_none = enif_make_atom(env,"none"); atom_notsup = enif_make_atom(env,"notsup"); diff --git a/lib/crypto/c_src/atoms.h b/lib/crypto/c_src/atoms.h index f5913de96f..0e2f1a0022 100644 --- a/lib/crypto/c_src/atoms.h +++ b/lib/crypto/c_src/atoms.h @@ -34,6 +34,10 @@ extern ERL_NIF_TERM atom_rsa_no_padding; extern ERL_NIF_TERM atom_signature_md; extern ERL_NIF_TERM atom_undefined; +extern ERL_NIF_TERM atom_hmac; +extern ERL_NIF_TERM atom_cmac; +extern ERL_NIF_TERM atom_poly1305; + extern ERL_NIF_TERM atom_ok; extern ERL_NIF_TERM atom_none; extern ERL_NIF_TERM atom_notsup; diff --git a/lib/crypto/c_src/bn.c b/lib/crypto/c_src/bn.c index 34ed4f7ebc..6021d56db6 100644 --- a/lib/crypto/c_src/bn.c +++ b/lib/crypto/c_src/bn.c @@ -32,8 +32,6 @@ int get_bn_from_mpint(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp) if (bin.size > INT_MAX - 4) goto err; - ERL_VALGRIND_ASSERT_MEM_DEFINED(bin.data, bin.size); - if (bin.size < 4) goto err; sz = (int)bin.size - 4; @@ -60,8 +58,6 @@ int get_bn_from_bin(ErlNifEnv* env, ERL_NIF_TERM term, BIGNUM** bnp) if (bin.size > INT_MAX) goto err; - ERL_VALGRIND_ASSERT_MEM_DEFINED(bin.data, bin.size); - if ((ret = BN_bin2bn(bin.data, (int)bin.size, NULL)) == NULL) goto err; @@ -103,8 +99,6 @@ ERL_NIF_TERM mod_exp_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) unsigned extra_byte; ERL_NIF_TERM ret; - ASSERT(argc == 4); - if (!get_bn_from_bin(env, argv[0], &bn_base)) goto bad_arg; if (!get_bn_from_bin(env, argv[1], &bn_exponent)) @@ -177,7 +171,6 @@ ERL_NIF_TERM bn2term(ErlNifEnv* env, const BIGNUM *bn) BN_bn2bin(bn, ptr); - ERL_VALGRIND_MAKE_MEM_DEFINED(ptr, dlen); return ret; err: diff --git a/lib/crypto/c_src/cipher.c b/lib/crypto/c_src/cipher.c index 0532fb7566..e144a891a6 100644 --- a/lib/crypto/c_src/cipher.c +++ b/lib/crypto/c_src/cipher.c @@ -214,7 +214,7 @@ ERL_NIF_TERM cipher_info_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[] if ((cipherp = get_cipher_type_no_key(argv[0])) == NULL) return enif_make_badarg(env); - if (FORBIDDEN_IN_FIPS(cipherp)) + if (CIPHER_FORBIDDEN_IN_FIPS(cipherp)) return enif_raise_exception(env, atom_notsup); if ((cipher = cipherp->cipher.p) == NULL) return enif_raise_exception(env, atom_notsup); @@ -330,7 +330,7 @@ ERL_NIF_TERM cipher_types_as_list(ErlNifEnv* env) for (p = cipher_types; (p->type.atom & (p->type.atom != atom_false)); p++) { if ((prev == p->type.atom) || - FORBIDDEN_IN_FIPS(p) ) + CIPHER_FORBIDDEN_IN_FIPS(p) ) continue; if ((p->cipher.p != NULL) || diff --git a/lib/crypto/c_src/cipher.h b/lib/crypto/c_src/cipher.h index 0e51c410eb..c23e128824 100644 --- a/lib/crypto/c_src/cipher.h +++ b/lib/crypto/c_src/cipher.h @@ -52,10 +52,10 @@ struct cipher_type_t { #ifdef FIPS_SUPPORT /* May have FIPS support, must check dynamically if it is enabled */ -# define FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_CIPHER) && FIPS_mode()) +# define CIPHER_FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_CIPHER) && FIPS_mode()) #else /* No FIPS support since the symbol FIPS_SUPPORT is undefined */ -# define FORBIDDEN_IN_FIPS(P) 0 +# define CIPHER_FORBIDDEN_IN_FIPS(P) 0 #endif extern ErlNifResourceType* evp_cipher_ctx_rtype; diff --git a/lib/crypto/c_src/cmac.c b/lib/crypto/c_src/cmac.c index 49e67ccf29..a1564f6661 100644 --- a/lib/crypto/c_src/cmac.c +++ b/lib/crypto/c_src/cmac.c @@ -18,71 +18,56 @@ * %CopyrightEnd% */ -#include "cmac.h" -#include "cipher.h" +#include "common.h" -ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key, Data) */ -#if defined(HAVE_CMAC) - const struct cipher_type_t *cipherp; - const EVP_CIPHER *cipher; - CMAC_CTX *ctx = NULL; - ErlNifBinary key; - ErlNifBinary data; - ERL_NIF_TERM ret; - size_t ret_size; - unsigned char *outp; - int cipher_len; +/***************************************************************** + * + * This file has functions for compatibility with cryptolibs + * lacking the EVP_Digest API. + * + * See mac.c for the implementation using the EVP interface. + * + ****************************************************************/ - ASSERT(argc == 3); +#if defined(HAVE_CMAC) && !defined(HAVE_EVP_PKEY_new_CMAC_key) - if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) - goto bad_arg; - if ((cipherp = get_cipher_type(argv[0], key.size)) == NULL) - goto bad_arg; - if (cipherp->flags & (NON_EVP_CIPHER | AEAD_CIPHER)) - goto bad_arg; - if (!enif_inspect_iolist_as_binary(env, argv[2], &data)) - goto bad_arg; +#include "cmac.h" - if (FORBIDDEN_IN_FIPS(cipherp)) - return enif_raise_exception(env, atom_notsup); - if ((cipher = cipherp->cipher.p) == NULL) - return enif_raise_exception(env, atom_notsup); +int cmac_low_level(ErlNifEnv* env, + ErlNifBinary key_bin, const EVP_CIPHER* cipher, ErlNifBinary text, + ErlNifBinary *ret_bin, int *ret_bin_alloc, ERL_NIF_TERM *return_term) +{ + CMAC_CTX *ctx = NULL; + size_t size; if ((ctx = CMAC_CTX_new()) == NULL) - goto err; - if (!CMAC_Init(ctx, key.data, key.size, cipher, NULL)) - goto err; - if (!CMAC_Update(ctx, data.data, data.size)) - goto err; - if ((cipher_len = EVP_CIPHER_block_size(cipher)) < 0) - goto err; - if ((outp = enif_make_new_binary(env, (size_t)cipher_len, &ret)) == NULL) - goto err; - if (!CMAC_Final(ctx, outp, &ret_size)) - goto err; + goto local_err; - ASSERT(ret_size == (unsigned)EVP_CIPHER_block_size(cipher)); - CONSUME_REDS(env, data); - goto done; + if (!CMAC_Init(ctx, key_bin.data, key_bin.size, cipher, NULL)) + goto local_err; - bad_arg: - return enif_make_badarg(env); + if (!CMAC_Update(ctx, text.data, text.size)) + goto local_err; - err: - ret = atom_notsup; + if ((size = (size_t)EVP_CIPHER_block_size(cipher)) < 0) + goto local_err; - done: + if (!enif_alloc_binary(size, ret_bin)) + goto local_err; + *ret_bin_alloc = 1; + + if (!CMAC_Final(ctx, ret_bin->data, &ret_bin->size)) + goto local_err; + + CMAC_CTX_free(ctx); + return 1; + + local_err: if (ctx) CMAC_CTX_free(ctx); - return ret; -#else - /* The CMAC functionality was introduced in OpenSSL 1.0.1 - * Although OTP requires at least version 0.9.8, the versions 0.9.8 and 1.0.0 are - * no longer maintained. */ - return atom_notsup; -#endif + *return_term = EXCP_ERROR(env,"Compat cmac"); + return 0; } +#endif diff --git a/lib/crypto/c_src/cmac.h b/lib/crypto/c_src/cmac.h index 14488def58..04c742b2dc 100644 --- a/lib/crypto/c_src/cmac.h +++ b/lib/crypto/c_src/cmac.h @@ -23,6 +23,12 @@ #include "common.h" -ERL_NIF_TERM cmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +#if defined(HAVE_CMAC) && !defined(HAVE_EVP_PKEY_new_CMAC_key) + +int cmac_low_level(ErlNifEnv* env, + ErlNifBinary key_bin, const EVP_CIPHER* cipher, ErlNifBinary text, + ErlNifBinary *ret_bin, int *ret_bin_alloc, ERL_NIF_TERM *return_term); + +#endif #endif /* E_CMAC_H__ */ diff --git a/lib/crypto/c_src/crypto.c b/lib/crypto/c_src/crypto.c index d533cba140..802818541b 100644 --- a/lib/crypto/c_src/crypto.c +++ b/lib/crypto/c_src/crypto.c @@ -31,7 +31,7 @@ #include "api_ng.h" #include "bn.h" #include "cipher.h" -#include "cmac.h" +#include "mac.h" #include "dh.h" #include "digest.h" #include "dss.h" @@ -46,7 +46,6 @@ #include "info.h" #include "math.h" #include "pkey.h" -#include "poly1305.h" #include "rand.h" #include "rsa.h" #include "srp.h" @@ -74,13 +73,10 @@ static ErlNifFunc nif_funcs[] = { {"hash_init_nif", 1, hash_init_nif, 0}, {"hash_update_nif", 2, hash_update_nif, 0}, {"hash_final_nif", 1, hash_final_nif, 0}, - {"hmac_nif", 3, hmac_nif, 0}, - {"hmac_nif", 4, hmac_nif, 0}, - {"hmac_init_nif", 2, hmac_init_nif, 0}, - {"hmac_update_nif", 2, hmac_update_nif, 0}, - {"hmac_final_nif", 1, hmac_final_nif, 0}, - {"hmac_final_nif", 2, hmac_final_nif, 0}, - {"cmac_nif", 3, cmac_nif, 0}, + {"mac_nif", 4, mac_nif, 0}, + {"mac_init_nif", 3, mac_init_nif, 0}, + {"mac_update_nif", 2, mac_update_nif, 0}, + {"mac_final_nif", 1, mac_final_nif, 0}, {"cipher_info_nif", 1, cipher_info_nif, 0}, {"aes_ige_crypt_nif", 4, aes_ige_crypt_nif, 0}, {"ng_crypto_init_nif", 4, ng_crypto_init_nif, 0}, @@ -112,8 +108,6 @@ static ErlNifFunc nif_funcs[] = { {"aead_cipher", 7, aead_cipher, 0}, - {"poly1305_nif", 2, poly1305_nif, 0}, - {"engine_by_id_nif", 1, engine_by_id_nif, 0}, {"engine_init_nif", 1, engine_init_nif, 0}, {"engine_finish_nif", 1, engine_finish_nif, 0}, @@ -181,9 +175,15 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) if (!enif_inspect_binary(env, tpl_array[1], &lib_bin)) return __LINE__; +#ifdef HAS_EVP_PKEY_CTX + if (!init_mac_ctx(env)) { + return __LINE__; + } +#else if (!init_hmac_ctx(env)) { return __LINE__; } +#endif if (!init_hash_ctx(env)) { return __LINE__; } @@ -248,6 +248,7 @@ static int initialize(ErlNifEnv* env, ERL_NIF_TERM load_info) #endif /* OPENSSL_THREADS */ init_digest_types(env); + init_mac_types(env); init_cipher_types(env); init_algorithms_types(env); diff --git a/lib/crypto/c_src/digest.c b/lib/crypto/c_src/digest.c index c987a664d5..0f887ab765 100644 --- a/lib/crypto/c_src/digest.c +++ b/lib/crypto/c_src/digest.c @@ -22,7 +22,7 @@ static struct digest_type_t digest_types[] = { - {{"md4"}, + {{"md4"}, NO_FIPS_DIGEST, #ifdef HAVE_MD4 {&EVP_md4} #else @@ -30,7 +30,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"md5"}, + {{"md5"}, NO_FIPS_DIGEST, #ifdef HAVE_MD5 {&EVP_md5} #else @@ -38,7 +38,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"ripemd160"}, + {{"ripemd160"}, NO_FIPS_DIGEST, #ifdef HAVE_RIPEMD160 {&EVP_ripemd160} #else @@ -46,9 +46,9 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha"}, {&EVP_sha1}}, + {{"sha"}, 0, {&EVP_sha1}}, - {{"sha224"}, + {{"sha224"}, 0, #ifdef HAVE_SHA224 {&EVP_sha224} #else @@ -56,7 +56,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha256"}, + {{"sha256"}, 0, #ifdef HAVE_SHA256 {&EVP_sha256} #else @@ -64,7 +64,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha384"}, + {{"sha384"}, 0, #ifdef HAVE_SHA384 {&EVP_sha384} #else @@ -72,7 +72,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha512"}, + {{"sha512"}, 0, #ifdef HAVE_SHA512 {&EVP_sha512} #else @@ -80,7 +80,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha3_224"}, + {{"sha3_224"}, 0, #ifdef HAVE_SHA3_224 {&EVP_sha3_224} #else @@ -88,7 +88,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha3_256"}, + {{"sha3_256"}, 0, #ifdef HAVE_SHA3_256 {&EVP_sha3_256} #else @@ -96,7 +96,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha3_384"}, + {{"sha3_384"}, 0, #ifdef HAVE_SHA3_384 {&EVP_sha3_384} #else @@ -104,7 +104,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"sha3_512"}, + {{"sha3_512"}, 0, #ifdef HAVE_SHA3_512 {&EVP_sha3_512} #else @@ -112,7 +112,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"blake2b"}, + {{"blake2b"}, 0, #ifdef HAVE_BLAKE2 {&EVP_blake2b512} #else @@ -120,7 +120,7 @@ static struct digest_type_t digest_types[] = #endif }, - {{"blake2s"}, + {{"blake2s"}, 0, #ifdef HAVE_BLAKE2 {&EVP_blake2s256} #else @@ -128,7 +128,8 @@ static struct digest_type_t digest_types[] = #endif }, - {{NULL}, {NULL}} + /*==== End of list ==== */ + {{NULL}, 0, {NULL}} }; void init_digest_types(ErlNifEnv* env) diff --git a/lib/crypto/c_src/digest.h b/lib/crypto/c_src/digest.h index 06852416cf..b1f8128a1f 100644 --- a/lib/crypto/c_src/digest.h +++ b/lib/crypto/c_src/digest.h @@ -28,12 +28,25 @@ struct digest_type_t { const char* str; /* before init, NULL for end-of-table */ ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ }type; + unsigned flags; union { const EVP_MD* (*funcp)(void); /* before init, NULL if notsup */ const EVP_MD* p; /* after init, NULL if notsup */ }md; }; +/* masks in the flags field if digest_type_t */ +#define NO_FIPS_DIGEST 1 + +#ifdef FIPS_SUPPORT +/* May have FIPS support, must check dynamically if it is enabled */ +# define DIGEST_FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_DIGEST) && FIPS_mode()) +#else +/* No FIPS support since the symbol FIPS_SUPPORT is undefined */ +# define DIGEST_FORBIDDEN_IN_FIPS(P) 0 +#endif + + void init_digest_types(ErlNifEnv* env); struct digest_type_t* get_digest_type(ERL_NIF_TERM type); diff --git a/lib/crypto/c_src/hmac.c b/lib/crypto/c_src/hmac.c index ff7005d75e..5e2c68bfee 100644 --- a/lib/crypto/c_src/hmac.c +++ b/lib/crypto/c_src/hmac.c @@ -18,6 +18,18 @@ * %CopyrightEnd% */ + +/***************************************************************** + * + * This file has functions for compatibility with cryptolibs + * lacking the EVP_Digest API. + * + * See mac.c for the implementation using the EVP interface. + * + ****************************************************************/ + +#ifndef HAS_EVP_PKEY_CTX + #include "hmac.h" #include "digest.h" @@ -47,61 +59,6 @@ int init_hmac_ctx(ErlNifEnv *env) { return 0; } -ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key, Data) or (Type, Key, Data, MacSize) */ - struct digest_type_t *digp = NULL; - ErlNifBinary key, data; - unsigned char buff[EVP_MAX_MD_SIZE]; - unsigned size = 0, req_size = 0; - ERL_NIF_TERM ret; - unsigned char *outp; - - ASSERT(argc == 3 || argc == 4); - - if ((digp = get_digest_type(argv[0])) == NULL) - goto bad_arg; - if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) - goto bad_arg; - if (key.size > INT_MAX) - goto bad_arg; - if (!enif_inspect_iolist_as_binary(env, argv[2], &data)) - goto bad_arg; - if (argc == 4) { - if (!enif_get_uint(env, argv[3], &req_size)) - goto bad_arg; - } - - if (digp->md.p == NULL) - goto err; - if (HMAC(digp->md.p, - key.data, (int)key.size, - data.data, data.size, - buff, &size) == NULL) - goto err; - - ASSERT(0 < size && size <= EVP_MAX_MD_SIZE); - CONSUME_REDS(env, data); - - if (argc == 4) { - if (req_size > size) - goto bad_arg; - - size = req_size; - } - - if ((outp = enif_make_new_binary(env, size, &ret)) == NULL) - goto err; - - memcpy(outp, buff, size); - return ret; - - bad_arg: - return enif_make_badarg(env); - - err: - return atom_notsup; -} - static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context *obj) { if (obj == NULL) @@ -118,17 +75,17 @@ static void hmac_context_dtor(ErlNifEnv* env, struct hmac_context *obj) } ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Type, Key) */ +{/* (hmac, Type, Key) */ struct digest_type_t *digp = NULL; ErlNifBinary key; ERL_NIF_TERM ret; struct hmac_context *obj = NULL; - ASSERT(argc == 2); + ASSERT(argc == 3); - if ((digp = get_digest_type(argv[0])) == NULL) + if ((digp = get_digest_type(argv[1])) == NULL) goto bad_arg; - if (!enif_inspect_iolist_as_binary(env, argv[1], &key)) + if (!enif_inspect_iolist_as_binary(env, argv[2], &key)) goto bad_arg; if (key.size > INT_MAX) goto bad_arg; @@ -268,3 +225,44 @@ ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) return ret; } + + +int hmac_low_level(ErlNifEnv* env, const EVP_MD *md, + ErlNifBinary key_bin, ErlNifBinary text, + ErlNifBinary *ret_bin, int *ret_bin_alloc, ERL_NIF_TERM *return_term) +{ + unsigned int size_int; + size_t size; + + /* Find the needed space */ + if (HMAC(md, + key_bin.data, (int)key_bin.size, + text.data, text.size, + NULL, &size_int) == NULL) + { + *return_term = EXCP_ERROR(env, "Get HMAC size failed"); + return 0; + } + + size = (size_t)size_int; /* Otherwise "size" is unused in 0.9.8.... */ + if (!enif_alloc_binary(size, ret_bin)) + { + *return_term = EXCP_ERROR(env, "Alloc binary"); + return 0; + } + *ret_bin_alloc = 1; + + /* And do the real HMAC calc */ + if (HMAC(md, + key_bin.data, (int)key_bin.size, + text.data, text.size, + ret_bin->data, &size_int) == NULL) + { + *return_term = EXCP_ERROR(env, "HMAC sign failed"); + return 0; + } + + return 1; +} + +#endif diff --git a/lib/crypto/c_src/hmac.h b/lib/crypto/c_src/hmac.h index 1f0e0ca632..f5805e13e5 100644 --- a/lib/crypto/c_src/hmac.h +++ b/lib/crypto/c_src/hmac.h @@ -21,13 +21,19 @@ #ifndef E_HMAC_H__ #define E_HMAC_H__ 1 +#ifndef HAS_EVP_PKEY_CTX + #include "common.h" int init_hmac_ctx(ErlNifEnv *env); -ERL_NIF_TERM hmac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM hmac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM hmac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); ERL_NIF_TERM hmac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +int hmac_low_level(ErlNifEnv* env, const EVP_MD *md, + ErlNifBinary key_bin, ErlNifBinary text, + ErlNifBinary *ret_bin, int *ret_bin_alloc, ERL_NIF_TERM *return_term); +#endif + #endif /* E_HMAC_H__ */ diff --git a/lib/crypto/c_src/mac.c b/lib/crypto/c_src/mac.c new file mode 100644 index 0000000000..149975ba9d --- /dev/null +++ b/lib/crypto/c_src/mac.c @@ -0,0 +1,751 @@ +/* + * %CopyrightBegin% + * + * Copyright Ericsson AB 2010-2019. 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% + */ + +#include "common.h" +#include "cipher.h" +#include "digest.h" +#include "cmac.h" +#include "hmac.h" +#include "mac.h" + +/*************************** + MAC type declaration +***************************/ + +struct mac_type_t { + union { + const char* str; /* before init, NULL for end-of-table */ + ERL_NIF_TERM atom; /* after init, 'false' for end-of-table */ + }name; + unsigned flags; + union { + const int pkey_type; + }alg; + int type; + size_t key_len; /* != 0 to also match on key_len */ +}; + +/* masks in the flags field if mac_type_t */ +#define NO_FIPS_MAC 1 + +#define NO_mac 0 +#define HMAC_mac 1 +#define CMAC_mac 2 +#define POLY1305_mac 3 + +static struct mac_type_t mac_types[] = +{ + {{"poly1305"}, NO_FIPS_MAC, +#ifdef HAVE_POLY1305 + /* If we have POLY then we have EVP_PKEY */ + {EVP_PKEY_POLY1305}, POLY1305_mac, 32 +#else + {EVP_PKEY_NONE}, NO_mac, 0 +#endif + }, + + {{"hmac"}, 0, +#ifdef HAS_EVP_PKEY_CTX + {EVP_PKEY_HMAC}, HMAC_mac, 0 +#else + /* HMAC is always supported, but possibly with low-level routines */ + {EVP_PKEY_NONE}, HMAC_mac, 0 +#endif + }, + + {{"cmac"}, 0, +#ifdef HAVE_CMAC + /* If we have CMAC then we have EVP_PKEY */ + {EVP_PKEY_CMAC}, CMAC_mac, 0 +#else + {EVP_PKEY_NONE}, NO_mac, 0 +#endif + }, + + /*==== End of list ==== */ + {{NULL}, 0, + {0}, NO_mac, 0 + } +}; + + +#ifdef FIPS_SUPPORT +/* May have FIPS support, must check dynamically if it is enabled */ +# define MAC_FORBIDDEN_IN_FIPS(P) (((P)->flags & NO_FIPS_MAC) && FIPS_mode()) +#else +/* No FIPS support since the symbol FIPS_SUPPORT is undefined */ +# define MAC_FORBIDDEN_IN_FIPS(P) 0 +#endif + + +/*************************** + Mandatory prototypes +***************************/ + +struct mac_type_t* get_mac_type(ERL_NIF_TERM type, size_t key_len); +struct mac_type_t* get_mac_type_no_key(ERL_NIF_TERM type); + +ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +ERL_NIF_TERM mac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + + +/******************************** + Support functions for type array +*********************************/ + +void init_mac_types(ErlNifEnv* env) +{ + struct mac_type_t* p = mac_types; + + for (p = mac_types; p->name.str; p++) { + p->name.atom = enif_make_atom(env, p->name.str); + } + p->name.atom = atom_false; /* end marker */ +} + + +ERL_NIF_TERM mac_types_as_list(ErlNifEnv* env) +{ + struct mac_type_t* p; + ERL_NIF_TERM prev, hd; + + hd = enif_make_list(env, 0); + prev = atom_undefined; + + for (p = mac_types; (p->name.atom & (p->name.atom != atom_false)); p++) { + if (prev == p->name.atom) + continue; + + if (p->type != NO_mac) + { + hd = enif_make_list_cell(env, p->name.atom, hd); + } + } + + return hd; +} + +struct mac_type_t* get_mac_type(ERL_NIF_TERM type, size_t key_len) +{ + struct mac_type_t* p = NULL; + for (p = mac_types; p->name.atom != atom_false; p++) { + if (type == p->name.atom) { + if ((p->key_len == 0) || (p->key_len == key_len)) + return p; + } + } + return NULL; +} + +struct mac_type_t* get_mac_type_no_key(ERL_NIF_TERM type) +{ + struct mac_type_t* p = NULL; + for (p = mac_types; p->name.atom != atom_false; p++) { + if (type == p->name.atom) { + return p; + } + } + return NULL; +} + +/******************************************************************* + * + * Mac nif + * + ******************************************************************/ +ERL_NIF_TERM mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (MacType, SubType, Key, Text) */ + ErlNifBinary text; + + if (!enif_inspect_iolist_as_binary(env, argv[3], &text)) + return EXCP_BADARG(env, "Bad text"); + + if (text.size > INT_MAX) + return EXCP_BADARG(env, "Too long text"); + + /* Run long jobs on a dirty scheduler to not block the current emulator thread */ + if (text.size > MAX_BYTES_TO_NIF) { + return enif_schedule_nif(env, "mac_one_time", + ERL_NIF_DIRTY_JOB_CPU_BOUND, + mac_one_time, argc, argv); + } + + return mac_one_time(env, argc, argv); +} + + + +ERL_NIF_TERM mac_one_time(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (MacType, SubType, Key, Text) */ + + struct mac_type_t *macp; + ErlNifBinary key_bin, text; + int ret_bin_alloc = 0; + ERL_NIF_TERM return_term; + const EVP_MD *md = NULL; + ErlNifBinary ret_bin; +#ifdef HAS_EVP_PKEY_CTX + size_t size; + EVP_PKEY *pkey = NULL; + EVP_MD_CTX *mctx = NULL; +#endif + + /*--------------------------------- + Get common indata and validate it + */ + if (!enif_inspect_iolist_as_binary(env, argv[2], &key_bin)) + { + return_term = EXCP_BADARG(env, "Bad key"); + goto err; + } + + if (!enif_inspect_iolist_as_binary(env, argv[3], &text)) + { + return_term = EXCP_BADARG(env, "Bad text"); + goto err; + } + + if (!(macp = get_mac_type(argv[0], key_bin.size))) + { + if (!get_mac_type_no_key(argv[0])) + return_term = EXCP_BADARG(env, "Unknown mac algorithm"); + else + return_term = EXCP_BADARG(env, "Bad key length"); + goto err; + } + + if (MAC_FORBIDDEN_IN_FIPS(macp)) + { + return_term = EXCP_NOTSUP(env, "MAC algorithm forbidden in FIPS"); + goto err; + } + + /*-------------------------------------------------- + Algorithm dependent indata checking and computation. + If EVP_PKEY is available, only set the pkey variable + and do the computation after the switch statement. + If not available, do the low-level calls in the + corresponding case part + */ + switch (macp->type) { + + /******** + * HMAC * + ********/ + case HMAC_mac: + { + struct digest_type_t *digp; + + if ((digp = get_digest_type(argv[1])) == NULL) + { + return_term = EXCP_BADARG(env, "Bad digest algorithm for HMAC"); + goto err; + } + if (digp->md.p == NULL) + { + return_term = EXCP_NOTSUP(env, "Unsupported digest algorithm"); + goto err; + } + if (DIGEST_FORBIDDEN_IN_FIPS(digp)) + { + return_term = EXCP_NOTSUP(env, "Digest algorithm for HMAC forbidden in FIPS"); + goto err; + } + md = digp->md.p; + +#ifdef HAS_EVP_PKEY_CTX +# ifdef HAVE_PKEY_new_raw_private_key + /* Prefered for new applications according to EVP_PKEY_new_mac_key(3) */ + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, /*engine*/ NULL, key_bin.data, key_bin.size); +# else + /* Available in older versions */ + pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, /*engine*/ NULL, key_bin.data, key_bin.size); +# endif + +#else + if (!hmac_low_level(env, md, key_bin, text, &ret_bin, &ret_bin_alloc, &return_term)) + goto err; + else + goto success; +#endif + } + break; + + + /******** + * CMAC * + ********/ +#ifdef HAVE_CMAC + case CMAC_mac: + { + const struct cipher_type_t *cipherp; + if (!(cipherp = get_cipher_type(argv[1], key_bin.size))) + { /* Something went wrong. Find out what by retrying in another way. */ + if (!get_cipher_type_no_key(argv[1])) + return_term = EXCP_BADARG(env, "Unknown cipher"); + else + /* Cipher exists, so it must be the key size that is wrong */ + return_term = EXCP_BADARG(env, "Bad key size"); + goto err; + } + + if (CIPHER_FORBIDDEN_IN_FIPS(cipherp)) + { + return_term = EXCP_NOTSUP(env, "Cipher algorithm not supported in FIPS"); + goto err; + } + + if (cipherp->cipher.p == NULL) + { + return_term = EXCP_NOTSUP(env, "Unsupported cipher algorithm"); + goto err; + } + +# ifdef HAVE_EVP_PKEY_new_CMAC_key + pkey = EVP_PKEY_new_CMAC_key(/*engine*/ NULL, key_bin.data, key_bin.size, cipherp->cipher.p); +# else + if (!cmac_low_level(env, key_bin, cipherp->cipher.p, text, &ret_bin, &ret_bin_alloc, &return_term)) + goto err; + else + goto success; +# endif + } + break; +#endif /* HAVE_CMAC */ + + + /************ + * POLY1305 * + ************/ +#ifdef HAVE_POLY1305 + case POLY1305_mac: + /* poly1305 implies that EVP_PKEY_new_raw_private_key exists */ + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_POLY1305, /*engine*/ NULL, key_bin.data, key_bin.size); + break; +#endif + + + /*************** + * Unknown MAC * + ***************/ + case NO_mac: + default: + /* We know that this mac is supported with some version(s) of cryptolib */ + return_term = EXCP_NOTSUP(env, "Unsupported mac algorithm"); + goto err; + } + + /*----------------------------------------- + Common computations when we have EVP_PKEY + */ +#ifdef HAS_EVP_PKEY_CTX + if (!pkey) + { + return_term = EXCP_ERROR(env, "EVP_PKEY_key creation"); + goto err; + } + + if ((mctx = EVP_MD_CTX_new()) == NULL) + { + return_term = EXCP_ERROR(env, "EVP_MD_CTX_new"); + goto err; + } + + if (EVP_DigestSignInit(mctx, /*&pctx*/ NULL, md, /*engine*/ NULL, pkey) != 1) + { + return_term = EXCP_ERROR(env, "EVP_DigestSign"); + goto err; + } + +# ifdef HAVE_DigestSign_as_single_op + if (EVP_DigestSign(mctx, NULL, &size, text.data, text.size) != 1) + { + return_term = EXCP_ERROR(env, "Can't get sign size"); + goto err; + } +# else + if (EVP_DigestSignUpdate(mctx, text.data, text.size) != 1) + { + return_term = EXCP_ERROR(env, "EVP_DigestSignUpdate"); + goto err; + } + + if (EVP_DigestSignFinal(mctx, NULL, &size) != 1) + { + return_term = EXCP_ERROR(env, "Can't get sign size"); + goto err; + } +# endif + + if (!enif_alloc_binary(size, &ret_bin)) + { + return_term = EXCP_ERROR(env, "Alloc binary"); + goto err; + } + ret_bin_alloc = 1; + +# ifdef HAVE_DigestSign_as_single_op + if (EVP_DigestSign(mctx, ret_bin.data, &size, text.data, text.size) != 1) +# else + if (EVP_DigestSignFinal(mctx, ret_bin.data, &size) != 1) +# endif + { + return_term = EXCP_ERROR(env, "Signing"); + goto err; + } + + goto success; /* The label "success:" could be left without any "goto success" + in some combination of flags. This prevents a compiler warning + */ +#endif /* ifdef HAS_EVP_PKEY_CTX */ + + + /**************************** + Exit when we got a signature + *****************************/ + success: + CONSUME_REDS(env, text); + + return_term = enif_make_binary(env, &ret_bin); + ret_bin_alloc = 0; + + err: + +#ifdef HAS_EVP_PKEY_CTX + if (pkey) + EVP_PKEY_free(pkey); + if (mctx) + EVP_MD_CTX_free(mctx); +#endif + + if (ret_bin_alloc) + enif_release_binary(&ret_bin); + + return return_term; +} + + +/******************************************************************* + * + * Mac ctx + * + ******************************************************************/ + +int init_mac_ctx(ErlNifEnv *env); + +struct mac_context +{ + EVP_MD_CTX *ctx; +}; + +static ErlNifResourceType* mac_context_rtype; + +static void mac_context_dtor(ErlNifEnv* env, struct mac_context*); + +int init_mac_ctx(ErlNifEnv *env) { + mac_context_rtype = enif_open_resource_type(env, NULL, "mac_context", + (ErlNifResourceDtor*) mac_context_dtor, + ERL_NIF_RT_CREATE|ERL_NIF_RT_TAKEOVER, + NULL); + if (mac_context_rtype == NULL) + goto err; + + return 1; + + err: + PRINTF_ERR0("CRYPTO: Could not open resource type 'mac_context'"); + return 0; +} + + +static void mac_context_dtor(ErlNifEnv* env, struct mac_context *obj) +{ + if (obj == NULL) + return; + + if (obj->ctx) + EVP_MD_CTX_free(obj->ctx); +} + +/******************************************************************* + * + * mac_init, mac_update, mac_final nifs + * + ******************************************************************/ + +ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (MacType, SubType, Key) */ +#ifdef HAS_EVP_PKEY_CTX + struct mac_context *obj = NULL; + struct mac_type_t *macp; + ErlNifBinary key_bin; + ERL_NIF_TERM return_term; + const EVP_MD *md = NULL; + EVP_PKEY *pkey = NULL; + + /*--------------------------------- + Get common indata and validate it + */ + if (!enif_inspect_iolist_as_binary(env, argv[2], &key_bin)) + { + return_term = EXCP_BADARG(env, "Bad key"); + goto err; + } + + if (!(macp = get_mac_type(argv[0], key_bin.size))) + { + if (!get_mac_type_no_key(argv[0])) + return_term = EXCP_BADARG(env, "Unknown mac algorithm"); + else + return_term = EXCP_BADARG(env, "Bad key length"); + goto err; + } + + if (MAC_FORBIDDEN_IN_FIPS(macp)) + { + return_term = EXCP_NOTSUP(env, "MAC algorithm forbidden in FIPS"); + goto err; + } + + /*-------------------------------------------------- + Algorithm dependent indata checking and computation. + If EVP_PKEY is available, only set the pkey variable + and do the computation after the switch statement. + If not available, do the low-level calls in the + corresponding case part + */ + switch (macp->type) { + + /******** + * HMAC * + ********/ + case HMAC_mac: + { + struct digest_type_t *digp; + + if ((digp = get_digest_type(argv[1])) == NULL) + { + return_term = EXCP_BADARG(env, "Bad digest algorithm for HMAC"); + goto err; + } + if (digp->md.p == NULL) + { + return_term = EXCP_NOTSUP(env, "Unsupported digest algorithm"); + goto err; + } + if (DIGEST_FORBIDDEN_IN_FIPS(digp)) + { + return_term = EXCP_NOTSUP(env, "Digest algorithm for HMAC forbidden in FIPS"); + goto err; + } + md = digp->md.p; + +# ifdef HAVE_PKEY_new_raw_private_key + /* Prefered for new applications according to EVP_PKEY_new_mac_key(3) */ + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_HMAC, /*engine*/ NULL, key_bin.data, key_bin.size); +# else + /* Available in older versions */ + pkey = EVP_PKEY_new_mac_key(EVP_PKEY_HMAC, /*engine*/ NULL, key_bin.data, key_bin.size); +# endif + } + break; + + + /******** + * CMAC * + ********/ +#if defined(HAVE_CMAC) && defined(HAVE_EVP_PKEY_new_CMAC_key) + case CMAC_mac: + { + const struct cipher_type_t *cipherp; + if (!(cipherp = get_cipher_type(argv[1], key_bin.size))) + { /* Something went wrong. Find out what by retrying in another way. */ + if (!get_cipher_type_no_key(argv[1])) + return_term = EXCP_BADARG(env, "Unknown cipher"); + else + /* Cipher exists, so it must be the key size that is wrong */ + return_term = EXCP_BADARG(env, "Bad key size"); + goto err; + } + + if (CIPHER_FORBIDDEN_IN_FIPS(cipherp)) + { + return_term = EXCP_NOTSUP(env, "Cipher algorithm not supported in FIPS"); + goto err; + } + + if (cipherp->cipher.p == NULL) + { + return_term = EXCP_NOTSUP(env, "Unsupported cipher algorithm"); + goto err; + } + + pkey = EVP_PKEY_new_CMAC_key(/*engine*/ NULL, key_bin.data, key_bin.size, cipherp->cipher.p); + } + break; +#endif /* HAVE_CMAC && HAVE_EVP_PKEY_new_CMAC_key */ + + + /************ + * POLY1305 * + ************/ +#ifdef HAVE_POLY1305 + case POLY1305_mac: + /* poly1305 implies that EVP_PKEY_new_raw_private_key exists */ + pkey = EVP_PKEY_new_raw_private_key(EVP_PKEY_POLY1305, /*engine*/ NULL, key_bin.data, key_bin.size); + break; +#endif + + + /*************** + * Unknown MAC * + ***************/ + case NO_mac: + default: + /* We know that this mac is supported with some version(s) of cryptolib */ + return_term = EXCP_NOTSUP(env, "Unsupported mac algorithm"); + goto err; + } + + /*----------------------------------------- + Common computations + */ + if (!pkey) + { + return_term = EXCP_ERROR(env, "EVP_PKEY_key creation"); + goto err; + } + + if ((obj = enif_alloc_resource(mac_context_rtype, sizeof(struct mac_context))) == NULL) + { + return_term = EXCP_ERROR(env, "Can't allocate mac_context_rtype"); + goto err; + } + + if ((obj->ctx = EVP_MD_CTX_new()) == NULL) + { + return_term = EXCP_ERROR(env, "EVP_MD_CTX_new"); + goto err; + } + + if (EVP_DigestSignInit(obj->ctx, /*&pctx*/ NULL, md, /*engine*/ NULL, pkey) != 1) + { + return_term = EXCP_ERROR(env, "EVP_DigestSign"); + goto err; + } + + return_term = enif_make_resource(env, obj); + + err: + + if (obj) + enif_release_resource(obj); + + if (pkey) + EVP_PKEY_free(pkey); + + return return_term; + +#else + if (argv[0] != atom_hmac) + return EXCP_NOTSUP(env, "Unsupported mac algorithm"); + + return hmac_init_nif(env, argc, argv); +#endif +} + + + +ERL_NIF_TERM mac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Ref, Text) */ + ErlNifBinary text; + + if (!enif_inspect_iolist_as_binary(env, argv[1], &text)) + return EXCP_BADARG(env, "Bad text"); + + if (text.size > INT_MAX) + return EXCP_BADARG(env, "Too long text"); + + /* Run long jobs on a dirty scheduler to not block the current emulator thread */ + if (text.size > MAX_BYTES_TO_NIF) { + return enif_schedule_nif(env, "mac_update", + ERL_NIF_DIRTY_JOB_CPU_BOUND, + mac_update, argc, argv); + } + + return mac_update(env, argc, argv); +} + + +ERL_NIF_TERM mac_update(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Ref, Text) */ +#ifdef HAS_EVP_PKEY_CTX + struct mac_context *obj = NULL; + ErlNifBinary text; + + if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)mac_context_rtype, (void**)&obj)) + return EXCP_BADARG(env, "Bad ref"); + + if (!enif_inspect_iolist_as_binary(env, argv[1], &text)) + return EXCP_BADARG(env, "Bad text"); + + if (EVP_DigestSignUpdate(obj->ctx, text.data, text.size) != 1) + return EXCP_ERROR(env, "EVP_DigestSignUpdate"); + + CONSUME_REDS(env, text); + return argv[0]; + +#else + return hmac_update_nif(env, argc, argv); +#endif +} + + + +ERL_NIF_TERM mac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) +{/* (Ref) */ +#ifdef HAS_EVP_PKEY_CTX + struct mac_context *obj; + size_t size; + ErlNifBinary ret_bin; + + if (!enif_get_resource(env, argv[0], (ErlNifResourceType*)mac_context_rtype, (void**)&obj)) + return EXCP_BADARG(env, "Bad ref"); + + if (EVP_DigestSignFinal(obj->ctx, NULL, &size) != 1) + return EXCP_ERROR(env, "Can't get sign size"); + + if (!enif_alloc_binary(size, &ret_bin)) + return EXCP_ERROR(env, "Alloc binary"); + + if (EVP_DigestSignFinal(obj->ctx, ret_bin.data, &size) != 1) + { + enif_release_binary(&ret_bin); + return EXCP_ERROR(env, "Signing"); + } + + return enif_make_binary(env, &ret_bin); + +#else + return hmac_final_nif(env, argc, argv); +#endif +} + diff --git a/lib/crypto/c_src/poly1305.h b/lib/crypto/c_src/mac.h index 4bf45e6218..053a331324 100644 --- a/lib/crypto/c_src/poly1305.h +++ b/lib/crypto/c_src/mac.h @@ -18,11 +18,21 @@ * %CopyrightEnd% */ -#ifndef E_POLY1305_H__ -#define E_POLY1305_H__ 1 +#ifndef E_MAC_H__ +#define E_MAC_H__ 1 #include "common.h" -ERL_NIF_TERM poly1305_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +int init_mac_ctx(ErlNifEnv *env); -#endif /* E_POLY1305_H__ */ +void init_mac_types(ErlNifEnv* env); + +ERL_NIF_TERM mac_types_as_list(ErlNifEnv* env); + +ERL_NIF_TERM mac_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +ERL_NIF_TERM mac_init_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM mac_update_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); +ERL_NIF_TERM mac_final_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]); + +#endif /* E_MAC_H__ */ diff --git a/lib/crypto/c_src/openssl_config.h b/lib/crypto/c_src/openssl_config.h index 339eb5b8f4..32a0830717 100644 --- a/lib/crypto/c_src/openssl_config.h +++ b/lib/crypto/c_src/openssl_config.h @@ -110,6 +110,12 @@ # define HAS_EVP_PKEY_CTX # define HAVE_EVP_CIPHER_CTX_COPY # endif + +# if OPENSSL_VERSION_NUMBER >= PACKED_OPENSSL_VERSION_PLAIN(1,1,1) +# define HAVE_PKEY_new_raw_private_key +# define HAVE_EVP_PKEY_new_CMAC_key +# define HAVE_DigestSign_as_single_op +# endif #endif diff --git a/lib/crypto/c_src/pkey.c b/lib/crypto/c_src/pkey.c index a1e2677b34..d53d91c25b 100644 --- a/lib/crypto/c_src/pkey.c +++ b/lib/crypto/c_src/pkey.c @@ -59,8 +59,9 @@ static int get_pkey_public_key(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_T EVP_PKEY **pkey); static int get_pkey_crypt_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM options, PKeyCryptOptions *opt); +#ifdef HAVE_RSA_SSLV23_PADDING static size_t size_of_RSA(EVP_PKEY *pkey); - +#endif static int get_pkey_digest_type(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NIF_TERM type, const EVP_MD **md) @@ -1031,6 +1032,7 @@ static int get_pkey_crypt_options(ErlNifEnv *env, ERL_NIF_TERM algorithm, ERL_NI return PKEY_BADARG; } +#ifdef HAVE_RSA_SSLV23_PADDING static size_t size_of_RSA(EVP_PKEY *pkey) { int ret = 0; RSA *rsa = NULL; @@ -1045,6 +1047,7 @@ static size_t size_of_RSA(EVP_PKEY *pkey) { return (ret < 0) ? 0 : (size_t)ret; } +#endif ERL_NIF_TERM pkey_crypt_nif(ErlNifEnv *env, int argc, const ERL_NIF_TERM argv[]) {/* (Algorithm, Data, PublKey=[E,N]|[E,N,D]|[E,N,D,P1,P2,E1,E2,C], Options, IsPrivate, IsEncrypt) */ diff --git a/lib/crypto/c_src/poly1305.c b/lib/crypto/c_src/poly1305.c deleted file mode 100644 index 76579c0a29..0000000000 --- a/lib/crypto/c_src/poly1305.c +++ /dev/null @@ -1,90 +0,0 @@ -/* - * %CopyrightBegin% - * - * Copyright Ericsson AB 2010-2018. 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% - */ - -#include "poly1305.h" - -/* For OpenSSL >= 1.1.1 the hmac_nif and cmac_nif could be integrated into poly1305 (with 'type' as parameter) */ -ERL_NIF_TERM poly1305_nif(ErlNifEnv* env, int argc, const ERL_NIF_TERM argv[]) -{/* (Key, Text) */ -#ifdef HAVE_POLY1305 - ErlNifBinary key_bin, text, ret_bin; - ERL_NIF_TERM ret; - EVP_PKEY *key = NULL; - EVP_MD_CTX *mctx = NULL; - EVP_PKEY_CTX *pctx = NULL; - const EVP_MD *md = NULL; - size_t size; - int ret_bin_alloc = 0; - - ASSERT(argc == 2); - - if (!enif_inspect_binary(env, argv[0], &key_bin)) - goto bad_arg; - if (key_bin.size != 32) - goto bad_arg; - if (!enif_inspect_binary(env, argv[1], &text)) - goto bad_arg; - - if ((key = EVP_PKEY_new_raw_private_key(EVP_PKEY_POLY1305, /*engine*/ NULL, key_bin.data, key_bin.size)) == NULL) - goto err; - - if ((mctx = EVP_MD_CTX_new()) == NULL) - goto err; - if (EVP_DigestSignInit(mctx, &pctx, md, /*engine*/ NULL, key) != 1) - goto err; - if (EVP_DigestSignUpdate(mctx, text.data, text.size) != 1) - goto err; - - if (EVP_DigestSignFinal(mctx, NULL, &size) != 1) - goto err; - if (!enif_alloc_binary(size, &ret_bin)) - goto err; - ret_bin_alloc = 1; - if (EVP_DigestSignFinal(mctx, ret_bin.data, &size) != 1) - goto err; - - if (size != ret_bin.size) { - if (!enif_realloc_binary(&ret_bin, size)) - goto err; - } - - ret = enif_make_binary(env, &ret_bin); - ret_bin_alloc = 0; - goto done; - - bad_arg: - return enif_make_badarg(env); - - err: - if (ret_bin_alloc) - enif_release_binary(&ret_bin); - ret = atom_error; - - done: - if (mctx) - EVP_MD_CTX_free(mctx); - if (key) - EVP_PKEY_free(key); - return ret; - -#else - return enif_raise_exception(env, atom_notsup); -#endif -} diff --git a/lib/crypto/doc/src/algorithm_details.xml b/lib/crypto/doc/src/algorithm_details.xml index 854bfbb4b1..71014764c8 100644 --- a/lib/crypto/doc/src/algorithm_details.xml +++ b/lib/crypto/doc/src/algorithm_details.xml @@ -37,122 +37,163 @@ <section> <title>Ciphers</title> + <p>A <seealso marker="crypto#type-cipher">cipher</seealso> in the + <seealso marker="crypto:new_api#the-new-api">new api</seealso> + is categorized as either + <seealso marker="crypto#type-cipher_no_iv">cipher_no_iv()</seealso>, + <seealso marker="crypto#type-cipher_iv">cipher_iv()</seealso> or + <seealso marker="crypto#type-cipher_aead">cipher_aead()</seealso>. + The letters IV are short for <i>Initialization Vector</i> and + AEAD is an abreviation of <i>Authenticated Encryption with Associated Data</i>. + </p> + <p>Due to irregular naming conventions, some cipher names in the old api are + substitued by new names in the new api. For a list of retired names, see + <seealso marker="crypto:new_api#retired-cipher-names">Retired cipher names</seealso>. + </p> + <p>To dynamically check availability, check that the name in the <i>Cipher and Mode</i> column is present in the + list returned by <seealso marker="crypto#supports-1">crypto:supports(ciphers)</seealso>. + </p> + <section> - <title>Block Ciphers</title> - <p>To be used in - <seealso marker="crypto#block_encrypt-3">block_encrypt/3</seealso>, - <seealso marker="crypto#block_encrypt-4">block_encrypt/4</seealso>, - <seealso marker="crypto#block_decrypt-3">block_decrypt/3</seealso> and - <seealso marker="crypto#block_decrypt-4">block_decrypt/4</seealso>. - </p> - <p>Available in all OpenSSL compatible with Erlang CRYPTO if not disabled by configuration. + <title>Ciphers without an IV - cipher_no_iv()</title> + <p>To be used with: </p> - <p>To dynamically check availability, check that the name in the <i>Cipher and Mode</i> column is present in the - list with the <c>cipher</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + <list> + <item><seealso marker="crypto#crypto_one_time/4">crypto_one_time/4</seealso></item> + <item><seealso marker="crypto#crypto_init/3">crypto_init/3</seealso></item> + </list> + <p>The ciphers are: </p> <table> - <row><cell><strong>Cipher and Mode</strong></cell><cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell><cell><strong>IV length</strong><br/><strong>[bytes]</strong></cell><cell><strong>Block size</strong><br/><strong>[bytes]</strong></cell></row> - <row><cell><c>aes_cbc</c></cell> <cell>16, 24, 32</cell><cell>16</cell><cell>16</cell></row> - <row><cell><c>aes_cbc128</c></cell><cell>16</cell><cell>16</cell><cell>16</cell></row> - <row><cell><c>aes_cbc256</c></cell><cell>32</cell><cell>16</cell><cell>16</cell></row> - - <row><cell><c>aes_cfb8</c></cell> <cell>16, 24, 32</cell><cell>16</cell><cell>any</cell></row> - - <row><cell><c>aes_ecb</c></cell><cell>16, 24, 32</cell><cell> </cell><cell>16</cell></row> - - <row><cell><c>aes_ige256</c></cell><cell>16</cell><cell>32</cell><cell>16</cell></row> - <row><cell><c>blowfish_cbc</c></cell> <cell>4-56</cell> <cell>8</cell> <cell>8</cell></row> - <row><cell><c>blowfish_cfb64</c></cell> <cell>≥1</cell> <cell>8</cell> <cell>any</cell></row> - <row><cell><c>blowfish_ecb</c></cell><cell>≥1</cell><cell> </cell><cell>8</cell></row> - <row><cell><c>blowfish_ofb64</c></cell><cell>≥1</cell><cell>8</cell><cell>any</cell></row> - - <row><cell><c>des3_cbc</c><br/><i>(=DES EDE3 CBC)</i></cell><cell>[8,8,8]</cell><cell>8</cell><cell>8</cell></row> - <row><cell><c>des3_cfb</c><br/><i>(=DES EDE3 CFB)</i></cell><cell>[8,8,8]</cell><cell>8</cell><cell>any</cell></row> - - <row><cell><c>des_cbc</c></cell><cell>8</cell><cell>8</cell> <cell>8</cell></row> - <row><cell><c>des_cfb</c></cell><cell>8</cell><cell>8</cell><cell>any</cell></row> - <row><cell><c>des_ecb</c></cell><cell>8</cell><cell> </cell><cell>8</cell></row> - <row><cell><c>des_ede3</c><br/><i>(=DES EDE3 CBC)</i></cell><cell>[8,8,8]</cell><cell>8</cell><cell>8</cell></row> - <row><cell><c>rc2_cbc</c></cell><cell>≥1</cell><cell>8</cell><cell>8</cell></row> - <tcaption>Block cipher key lengths</tcaption> + <row> + <cell><strong>Cipher and Mode</strong></cell> + <cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Block size</strong><br/><strong>[bytes]</strong></cell> + </row> + <row><cell><c>aes_128_ecb</c></cell> <cell>16</cell> <cell>16</cell></row> + <row><cell><c>aes_192_ecb</c></cell> <cell>24</cell> <cell>16</cell></row> + <row><cell><c>aes_256_ecb</c></cell> <cell>32</cell> <cell>16</cell></row> + <row><cell><c>blowfish_ecb</c></cell> <cell>16</cell> <cell> 8</cell></row> + <row><cell><c>des_ecb</c></cell> <cell> 8</cell> <cell> 8</cell></row> + <row><cell><c>rc4</c></cell> <cell>16</cell> <cell> 1</cell></row> + <tcaption>Ciphers without IV</tcaption> </table> </section> <section> - <title>AEAD Ciphers</title> - <p>To be used in <seealso marker="crypto#block_encrypt-4">block_encrypt/4</seealso> and - <seealso marker="crypto#block_decrypt-4">block_decrypt/4</seealso>. + <title>Ciphers with an IV - cipher_iv()</title> + <p>To be used with: </p> - <p>To dynamically check availability, check that the name in the <i>Cipher and Mode</i> column is present in the - list with the <c>cipher</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + <list> + <item><seealso marker="crypto#crypto_one_time/5">crypto_one_time/5</seealso></item> + <item><seealso marker="crypto#crypto_init/4">crypto_init/4</seealso></item> + <item><seealso marker="crypto#crypto_dyn_iv_init/3">crypto_dyn_iv_init/3</seealso></item> + </list> + <p>The ciphers are: </p> <table> - <row><cell><strong>Cipher and Mode</strong></cell><cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell><cell><strong>IV length</strong><br/><strong>[bytes]</strong></cell><cell><strong>AAD length</strong><br/><strong>[bytes]</strong></cell><cell><strong>Tag length</strong><br/><strong>[bytes]</strong></cell><cell><strong>Block size</strong><br/><strong>[bytes]</strong></cell><cell><strong>Supported with</strong><br/><strong>OpenSSL versions</strong></cell></row> - <row><cell><c>aes_ccm</c></cell> <cell>16,24,32</cell> <cell>7-13</cell> <cell>any</cell> <cell>even 4-16<br/>default: 12</cell> <cell>any</cell><cell>≥1.1.0</cell></row> - <row><cell><c>aes_gcm</c></cell> <cell>16,24,32</cell> <cell>≥1</cell> <cell>any</cell> <cell>1-16<br/>default: 16</cell> <cell>any</cell><cell>≥1.1.0</cell></row> - <row><cell><c>chacha20_poly1305</c></cell><cell>32</cell> <cell>1-16</cell> <cell>any</cell> <cell>16</cell> <cell>any</cell><cell>≥1.1.0</cell></row> - <tcaption>AEAD cipher key lengths</tcaption> + <row> + <cell><strong>Cipher and Mode</strong></cell> + <cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>IV length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Block size</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Limited to</strong><br/><strong>OpenSSL versions</strong></cell> + </row> + <row><cell><c>aes_128_cbc</c></cell> <cell>16</cell> <cell>16</cell> <cell>16</cell> <cell></cell></row> + <row><cell><c>aes_192_cbc</c></cell> <cell>24</cell> <cell>16</cell> <cell>16</cell> <cell></cell></row> + <row><cell><c>aes_256_cbc</c></cell> <cell>32</cell> <cell>16</cell> <cell>16</cell> <cell></cell></row> + <row><cell><c>aes_128_cfb8</c></cell> <cell>16</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_192_cfb8</c></cell> <cell>24</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_256_cfb8</c></cell> <cell>32</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_128_cfb128</c></cell><cell>16</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_192_cfb128</c></cell><cell>24</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_256_cfb128</c></cell><cell>32</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_128_ctr</c></cell> <cell>16</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_192_ctr</c></cell> <cell>24</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_256_ctr</c></cell> <cell>32</cell> <cell>16</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>aes_ige256</c></cell> <cell>16</cell> <cell>32</cell> <cell>16</cell> <cell></cell></row> + <row><cell><c>blowfish_cbc</c></cell> <cell>16</cell> <cell> 8</cell> <cell> 8</cell> <cell></cell></row> + <row><cell><c>blowfish_cfb64</c></cell><cell>16</cell> <cell> 8</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>blowfish_ofb64</c></cell><cell>16</cell> <cell> 8</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>chacha20</c></cell> <cell>32</cell> <cell>16</cell> <cell> 1</cell> <cell>≥1.1.0d</cell></row> + <row><cell><c>des_cbc</c></cell> <cell> 8</cell> <cell> 8</cell> <cell> 8</cell> <cell></cell></row> + <row><cell><c>des_ede3_cbc</c></cell> <cell>24</cell> <cell> 8</cell> <cell> 8</cell> <cell></cell></row> + <row><cell><c>des_cfb</c></cell> <cell> 8</cell> <cell> 8</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>des_ede3_cfb</c></cell> <cell>24</cell> <cell> 8</cell> <cell> 1</cell> <cell></cell></row> + <row><cell><c>rc2_cbc</c></cell> <cell>16</cell> <cell> 8</cell> <cell> 8</cell> <cell></cell></row> + <tcaption>Ciphers with IV</tcaption> </table> </section> <section> - <title>Stream Ciphers</title> - <p>To be used in <seealso marker="crypto#stream_init-2">stream_init/2</seealso> and - <seealso marker="crypto#stream_init/3">stream_init/3</seealso>. + <title>Ciphers with AEAD - cipher_aead()</title> + <p>To be used with: </p> - <p>To dynamically check availability, check that the name in the <i>Cipher and Mode</i> column is present in the - list with the <c>cipher</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + <list> + <item><seealso marker="crypto#crypto_one_time_aead/6">crypto_one_time_aead/6</seealso></item> + <item><seealso marker="crypto#crypto_one_time_aead/7">crypto_one_time_aead/7</seealso></item> + </list> + <p>The ciphers are: </p> <table> - <row><cell><strong>Cipher and Mode</strong></cell><cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell><cell><strong>IV length</strong><br/><strong>[bytes]</strong></cell><cell><strong>Supported with</strong><br/><strong>OpenSSL versions</strong></cell></row> - <row><cell><c>aes_ctr</c></cell><cell>16, 24, 32</cell><cell>16</cell><cell>≥1.0.1</cell></row> - <row><cell><c>rc4</c></cell><cell>≥1</cell><cell> </cell> <cell>all</cell></row> - <tcaption>Stream cipher key lengths</tcaption> + <row> + <cell><strong>Cipher and Mode</strong></cell> + <cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>IV length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>AAD length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Tag length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Block size</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Limited to</strong><br/><strong>OpenSSL versions</strong></cell> + </row> + <row><cell><c>aes_128_ccm</c></cell> <cell>16</cell> <cell>7-13</cell> <cell>any</cell> <cell>even 4-16<br/>default: 12</cell> <cell>any</cell><cell>≥1.0.1</cell></row> + <row><cell><c>aes_192_ccm</c></cell> <cell>24</cell> <cell>7-13</cell> <cell>any</cell> <cell>even 4-16<br/>default: 12</cell> <cell>any</cell><cell>≥1.0.1</cell></row> + <row><cell><c>aes_256_ccm</c></cell> <cell>32</cell> <cell>7-13</cell> <cell>any</cell> <cell>even 4-16<br/>default: 12</cell> <cell>any</cell><cell>≥1.0.1</cell></row> + + <row><cell><c>aes_128_gcm</c></cell> <cell>16</cell> <cell>≥1</cell> <cell>any</cell> <cell>1-16<br/>default: 16</cell> <cell>any</cell><cell>≥1.0.1</cell></row> + <row><cell><c>aes_192_gcm</c></cell> <cell>24</cell> <cell>≥1</cell> <cell>any</cell> <cell>1-16<br/>default: 16</cell> <cell>any</cell><cell>≥1.0.1</cell></row> + <row><cell><c>aes_256_gcm</c></cell> <cell>32</cell> <cell>≥1</cell> <cell>any</cell> <cell>1-16<br/>default: 16</cell> <cell>any</cell><cell>≥1.0.1</cell></row> + + <row><cell><c>chacha20_poly1305</c></cell><cell>32</cell> <cell>1-16</cell> <cell>any</cell> <cell>16</cell> <cell>any</cell><cell>≥1.1.0</cell></row> + <tcaption>AEAD ciphers</tcaption> </table> </section> </section> + <section> <title>Message Authentication Codes (MACs)</title> + <p>To be used in <seealso marker="crypto#mac-4">mac/4</seealso> and + <seealso marker="crypto:new_api#macs--message-authentication-codes-">related functions</seealso>. + </p> <section> <title>CMAC</title> - <p>To be used in <seealso marker="crypto#cmac-3">cmac/3</seealso> and - <seealso marker="crypto#cmac-3">cmac/4</seealso>. - </p> <p>CMAC with the following ciphers are available with OpenSSL 1.0.1 or later if not disabled by configuration. </p> <p>To dynamically check availability, check that the name <c>cmac</c> is present in the - list with the <c>macs</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(macs)</seealso>. Also check that the name in the <i>Cipher and Mode</i> column is present in the - list with the <c>cipher</c> tag in the return value. + list returned by <seealso marker="crypto#supports-1">crypto:supports(ciphers)</seealso>. </p> <table> - <row><cell><strong>Cipher and Mode</strong></cell><cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell><cell><strong>Max Mac Length</strong><br/><strong>[bytes]</strong></cell></row> - <row><cell><c>aes_cbc</c></cell> <cell>16, 24, 32</cell><cell>16</cell></row> - <row><cell><c>aes_cbc128</c></cell><cell>16</cell><cell>16</cell></row> - <row><cell><c>aes_cbc256</c></cell><cell>32</cell><cell>16</cell></row> - - <row><cell><c>aes_cfb8</c></cell> <cell>16</cell><cell>1</cell></row> - - <row><cell><c>blowfish_cbc</c></cell> <cell>4-56</cell> <cell>8</cell></row> - <row><cell><c>blowfish_cfb64</c></cell> <cell>≥1</cell> <cell>1</cell></row> - <row><cell><c>blowfish_ecb</c></cell><cell>≥1</cell> <cell>8</cell></row> - <row><cell><c>blowfish_ofb64</c></cell><cell>≥1</cell> <cell>1</cell></row> - - <row><cell><c>des3_cbc</c><br/><i>(=DES EDE3 CBC)</i></cell><cell>[8,8,8]</cell><cell>8</cell></row> - <row><cell><c>des3_cfb</c><br/><i>(=DES EDE3 CFB)</i></cell><cell>[8,8,8]</cell><cell>1</cell></row> - - <row><cell><c>des_cbc</c></cell><cell>8</cell><cell>8</cell></row> - - <row><cell><c>des_cfb</c></cell><cell>8</cell><cell>1</cell></row> - <row><cell><c>des_ecb</c></cell><cell>8</cell><cell>1</cell></row> - <row><cell><c>rc2_cbc</c></cell><cell>≥1</cell><cell>8</cell></row> + <row> + <cell><strong>Cipher and Mode</strong></cell> + <cell><strong>Key length</strong><br/><strong>[bytes]</strong></cell> + <cell><strong>Max Mac Length</strong><br/><strong>(= default length)</strong><br/><strong>[bytes]</strong></cell> + </row> + <row><cell><c>aes_128_cbc</c></cell> <cell>16</cell> <cell>16</cell></row> + <row><cell><c>aes_192_cbc</c></cell> <cell>24</cell> <cell>16</cell></row> + <row><cell><c>aes_256_cbc</c></cell> <cell>32</cell> <cell>16</cell></row> + <row><cell><c>aes_128_ecb</c></cell> <cell>16</cell> <cell>16</cell></row> + <row><cell><c>aes_192_ecb</c></cell> <cell>24</cell> <cell>16</cell></row> + <row><cell><c>aes_256_ecb</c></cell> <cell>32</cell> <cell>16</cell></row> + <row><cell><c>blowfish_cbc</c></cell> <cell>16</cell> <cell> 8</cell></row> + <row><cell><c>blowfish_ecb</c></cell> <cell>16</cell> <cell> 8</cell></row> + <row><cell><c>des_cbc</c></cell> <cell> 8</cell> <cell> 8</cell></row> + <row><cell><c>des_ecb</c></cell> <cell> 8</cell> <cell> 8</cell></row> + <row><cell><c>des_ede3_cbc</c></cell> <cell>24</cell> <cell> 8</cell></row> + <row><cell><c>rc2_cbc</c></cell> <cell>16</cell> <cell> 8</cell></row> <tcaption>CMAC cipher key lengths</tcaption> </table> </section> @@ -162,9 +203,34 @@ <p>Available in all OpenSSL compatible with Erlang CRYPTO if not disabled by configuration. </p> <p>To dynamically check availability, check that the name <c>hmac</c> is present in the - list with the <c>macs</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(macs)</seealso> and + that the hash name is present in the + list returned by <seealso marker="crypto#supports-1">crypto:supports(hashs)</seealso>. </p> + + <table> + <row> + <cell><strong>Hash</strong></cell> + <cell><strong>Max Mac Length</strong><br/><strong>(= default length)</strong><br/><strong>[bytes]</strong></cell> + </row> + <row><cell><c>sha</c></cell> <cell>20</cell></row> + <row><cell><c>sha224</c></cell> <cell>28</cell></row> + <row><cell><c>sha256</c></cell> <cell>32</cell></row> + <row><cell><c>sha384</c></cell> <cell>48</cell></row> + <row><cell><c>sha512</c></cell> <cell>64</cell></row> + <row><cell><c>sha3_224</c></cell> <cell>28</cell></row> + <row><cell><c>sha3_256</c></cell> <cell>32</cell></row> + <row><cell><c>sha3_384</c></cell> <cell>48</cell></row> + <row><cell><c>sha3_512</c></cell> <cell>64</cell></row> + <row><cell><c>blake2b</c></cell> <cell>64</cell></row> + <row><cell><c>blake2s</c></cell> <cell>32</cell></row> + <row><cell><c>md4</c></cell> <cell>16</cell></row> + <row><cell><c>md5</c></cell> <cell>16</cell></row> + <row><cell><c>ripemd160</c></cell> <cell>20</cell></row> + <tcaption>HMAC output sizes</tcaption> + </table> + + </section> <section> @@ -172,8 +238,9 @@ <p>POLY1305 is available with OpenSSL 1.1.1 or later if not disabled by configuration. </p> <p>To dynamically check availability, check that the name <c>poly1305</c> is present in the - list with the <c>macs</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(macs)</seealso>. + </p> + <p>The poly1305 mac wants an 32 bytes key and produces a 16 byte MAC by default. </p> </section> @@ -183,22 +250,20 @@ <title>Hash</title> <p>To dynamically check availability, check that the wanted name in the <i>Names</i> column is present in the - list with the <c>hashs</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(hashs)</seealso>. </p> - <table> <row><cell><strong>Type</strong></cell> <cell><strong>Names</strong></cell> - <cell><strong>Supported with</strong><br/><strong>OpenSSL versions</strong></cell> + <cell><strong>Limitated to</strong><br/><strong>OpenSSL versions</strong></cell> </row> - <row><cell>SHA1</cell><cell>sha</cell><cell>all</cell></row> - <row><cell>SHA2</cell><cell>sha224, sha256, sha384, sha512</cell><cell>all</cell></row> + <row><cell>SHA1</cell><cell>sha</cell><cell></cell></row> + <row><cell>SHA2</cell><cell>sha224, sha256, sha384, sha512</cell><cell></cell></row> <row><cell>SHA3</cell><cell>sha3_224, sha3_256, sha3_384, sha3_512</cell><cell>≥1.1.1</cell></row> - <row><cell>MD4</cell><cell>md4</cell><cell>all</cell></row> - <row><cell>MD5</cell><cell>md5</cell><cell>all</cell></row> - <row><cell>RIPEMD</cell><cell>ripemd160</cell><cell>all</cell></row> + <row><cell>MD4</cell><cell>md4</cell><cell></cell></row> + <row><cell>MD5</cell><cell>md5</cell><cell></cell></row> + <row><cell>RIPEMD</cell><cell>ripemd160</cell><cell></cell></row> <tcaption></tcaption> </table> </section> @@ -210,8 +275,7 @@ <title>RSA</title> <p>RSA is available with all OpenSSL versions compatible with Erlang CRYPTO if not disabled by configuration. To dynamically check availability, check that the atom <c>rsa</c> is present in the - list with the <c>public_keys</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(public_keys)</seealso>. </p> <warning> <!-- In RefMan rsa_opt(), rsa_sign_verify_opt() and User's man RSA --> @@ -283,8 +347,7 @@ <title>DSS</title> <p>DSS is available with OpenSSL versions compatible with Erlang CRYPTO if not disabled by configuration. To dynamically check availability, check that the atom <c>dss</c> is present in the - list with the <c>public_keys</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(public_keys)</seealso>. </p> </section> @@ -292,13 +355,11 @@ <title>ECDSA</title> <p>ECDSA is available with OpenSSL 0.9.8o or later if not disabled by configuration. To dynamically check availability, check that the atom <c>ecdsa</c> is present in the - list with the <c>public_keys</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. - If the atom <c>ec_gf2m</c> characteristic two field curves are available. + list returned by <seealso marker="crypto#supports-1">crypto:supports(public_keys)</seealso>. + If the atom <c>ec_gf2m</c> also is present, the characteristic two field curves are available. </p> - <p>The actual supported named curves could be checked by examining the list with the - <c>curves</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + <p>The actual supported named curves could be checked by examining the + list returned by <seealso marker="crypto#supports-1">crypto:supports(curves)</seealso>. </p> </section> @@ -306,13 +367,11 @@ <title>EdDSA</title> <p>EdDSA is available with OpenSSL 1.1.1 or later if not disabled by configuration. To dynamically check availability, check that the atom <c>eddsa</c> is present in the - list with the <c>public_keys</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(public_keys)</seealso>. </p> <p>Support for the curves ed25519 and ed448 is implemented. The actual supported named curves could be checked by examining the list with the - <c>curves</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(curves)</seealso>. </p> </section> @@ -321,8 +380,7 @@ <p>Diffie-Hellman computations are available with OpenSSL versions compatible with Erlang CRYPTO if not disabled by configuration. To dynamically check availability, check that the atom <c>dh</c> is present in the - list with the <c>public_keys</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(public_keys)</seealso>. </p> </section> @@ -330,17 +388,15 @@ <title>Elliptic Curve Diffie-Hellman</title> <p>Elliptic Curve Diffie-Hellman is available with OpenSSL 0.9.8o or later if not disabled by configuration. To dynamically check availability, check that the atom <c>ecdh</c> is present in the - list with the <c>public_keys</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + list returned by <seealso marker="crypto#supports-1">crypto:supports(public_keys)</seealso>. </p> <p>The Edward curves <c>x25519</c> and <c>x448</c> are supported with OpenSSL 1.1.1 or later if not disabled by configuration. </p> - <p>The actual supported named curves could be checked by examining the list with the - <c>curves</c> tag in the return value of - <seealso marker="crypto#supports-0">crypto:supports()</seealso>. + <p>The actual supported named curves could be checked by examining the + list returned by <seealso marker="crypto#supports-1">crypto:supports(curves)</seealso>. </p> </section> diff --git a/lib/crypto/doc/src/crypto.xml b/lib/crypto/doc/src/crypto.xml index d1d1252f29..3973cf3f9f 100644 --- a/lib/crypto/doc/src/crypto.xml +++ b/lib/crypto/doc/src/crypto.xml @@ -302,6 +302,12 @@ </datatype> <datatype> + <name name="cmac_cipher_algorithm"/> + <desc> + </desc> + </datatype> + + <datatype> <name name="rsa_digest_type"/> <desc> </desc> @@ -324,6 +330,11 @@ <name name="sha2"/> <name name="sha3"/> <name name="blake2"/> + <desc> + </desc> + </datatype> + + <datatype> <name name="compatibility_only_hash"/> <desc> <p>The <c>compatibility_only_hash()</c> algorithms are recommended only for compatibility with existing applications.</p> @@ -575,10 +586,11 @@ <datatype_title>Internal data types</datatype_title> <datatype> - <name name="stream_state"/> - <name name="hmac_state"/> - <name name="hash_state"/> <name name="crypto_state"/> + <name name="hash_state"/> + <name name="hmac_state"/> + <name name="mac_state"/> + <name name="stream_state"/> <desc> <p>Contexts with an internal state that should not be manipulated but passed between function calls. </p> @@ -783,6 +795,187 @@ </desc> </func> + <func> + <name name="mac" arity="3" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Short for <seealso marker="#mac-4">mac(Type, undefined, Key, Data)</seealso>. + </p> + </desc> + </func> + + <func> + <name name="mac" arity="4" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Computes a MAC (Message Authentication Code) of type <c>Type</c> from <c>Data</c>. + </p> + + <p><c>SubType</c> depends on the MAC <c>Type</c>: + </p> + <list> + <item>For <c>hmac</c> it is a hash algorithm, see + <seealso marker="algorithm_details#hmac">Algorithm Details</seealso> in the User's Guide. + </item> + <item>For <c>cmac</c> it is a cipher suitable for cmac, see + <seealso marker="algorithm_details#cmac">Algorithm Details</seealso> in the User's Guide. + </item> + <item>For <c>poly1305</c> it should be set to <c>undefined</c> or the + <seealso marker="#mac_init-2">mac/2</seealso> function could be used instead, see + <seealso marker="algorithm_details#poly1305">Algorithm Details</seealso> in the User's Guide. + </item> + </list> + + <p><c>Key</c> is the authentication key with a length according to the + <c>Type</c> and <c>SubType</c>. + The key length could be found with the + <seealso marker="#hash_info-1">hash_info/1</seealso> (<c>hmac</c>) for and + <seealso marker="#cipher_info-1">cipher_info/1</seealso> (<c>cmac</c>) + functions. For <c>poly1305</c> the key length is 32 bytes. Note that + the cryptographic quality of the key is not checked. + </p> + + <p>The <c>Mac</c> result will have a default length depending on the <c>Type</c> and <c>SubType</c>. + To set a shorter length, use <seealso marker="#macN-4">macN/4</seealso> or + <seealso marker="#macN-5">macN/5</seealso> instead. + The default length is documented in + <seealso marker="algorithm_details#message-authentication-codes--macs-">Algorithm Details</seealso> + in the User's Guide. + </p> + </desc> + </func> + + <func> + <name name="macN" arity="4" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Short for <seealso marker="#macN-5">macN(Type, undefined, Key, Data, MacLength)</seealso>. + </p> + </desc> + </func> + + <func> + <name name="macN" arity="5" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Computes a MAC (Message Authentication Code) + as <seealso marker="#mac-3">mac/3</seealso> and <seealso marker="#mac-4">mac/4</seealso> but + <c>MacLength</c> will limit the size of the resultant <c>Mac</c> to + at most <c>MacLength</c> bytes. + Note that if <c>MacLength</c> is greater than the actual number of + bytes returned from the underlying hash, the returned hash will have + that shorter length instead. + </p> + <p>The max <c>MacLength</c> is documented in + <seealso marker="algorithm_details#message-authentication-codes--macs-">Algorithm Details</seealso> + in the User's Guide. + </p> + </desc> + </func> + + <func> + <name name="mac_init" arity="2" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Short for <seealso marker="#mac_init-3">mac_init(Type, undefined, Key)</seealso>. + </p> + </desc> + </func> + + <func> + <name name="mac_init" arity="3" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Initializes the context for streaming MAC operations. + </p> + <p><c>Type</c> determines which mac algorithm to use in the MAC operation. + </p> + + <p><c>SubType</c> depends on the MAC <c>Type</c>: + </p> + <list> + <item>For <c>hmac</c> it is a hash algorithm, see + <seealso marker="algorithm_details#hmac">Algorithm Details</seealso> in the User's Guide. + </item> + <item>For <c>cmac</c> it is a cipher suitable for cmac, see + <seealso marker="algorithm_details#cmac">Algorithm Details</seealso> in the User's Guide. + </item> + <item>For <c>poly1305</c> it should be set to <c>undefined</c> or the + <seealso marker="#mac_init-2">mac/2</seealso> function could be used instead, see + <seealso marker="algorithm_details#poly1305">Algorithm Details</seealso> in the User's Guide. + </item> + </list> + + <p><c>Key</c> is the authentication key with a length according to the + <c>Type</c> and <c>SubType</c>. + The key length could be found with the + <seealso marker="#hash_info-1">hash_info/1</seealso> (<c>hmac</c>) for and + <seealso marker="#cipher_info-1">cipher_info/1</seealso> (<c>cmac</c>) + functions. For <c>poly1305</c> the key length is 32 bytes. Note that + the cryptographic quality of the key is not checked. + </p> + + <p>The returned <c>State</c> should be used in one or more subsequent calls to + <seealso marker="#mac_update-2">mac_update/2</seealso>. + The MAC value is finally returned by calling + <seealso marker="#mac_final-1">mac_final/1</seealso> or + <seealso marker="#mac_finalN-2">mac_finalN/2</seealso>. + </p> + + <p>See <seealso marker="crypto:new_api#example-of-mac_init-mac_update-and-mac_final"> + examples in the User's Guide.</seealso> + </p> + </desc> + </func> + + <func> + <name name="mac_update" arity="2" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Updates the MAC represented by <c>State0</c> using the given <c>Data</c> which + could be of any length. + </p> + <p>The <c>State0</c> is the State value originally from a MAC init function, that is + <seealso marker="#mac_init-2">mac_init/2</seealso>, + <seealso marker="#mac_init-3">mac_init/3</seealso> or + a previous call of <c>mac_update/2</c>. + The value <c>State0</c> is returned unchanged by the function as <c>State</c>. + </p> + </desc> + </func> + + <func> + <name name="mac_final" arity="1" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Finalizes the MAC operation referenced by <c>State</c>. The <c>Mac</c> result will have + a default length depending on the <c>Type</c> and <c>SubType</c> in the + <seealso marker="#mac_init-3">mac_init/2,3</seealso> call. + To set a shorter length, use <seealso marker="#mac_finalN-2">mac_finalN/2</seealso> instead. + The default length is documented in + <seealso marker="algorithm_details#message-authentication-codes--macs-">Algorithm Details</seealso> + in the User's Guide. + </p> + </desc> + </func> + + <func> + <name name="mac_finalN" arity="2" since="OTP @OTP-13872@"/> + <fsummary></fsummary> + <desc> + <p>Finalizes the MAC operation referenced by <c>State</c>. + </p> + <p><c>Mac</c> will be a binary with at most <c>MacLength</c> bytes. + Note that if <c>MacLength</c> is greater than the actual number of + bytes returned from the underlying hash, the returned hash will have + that shorter length instead. + </p> + <p>The max <c>MacLength</c> is documented in + <seealso marker="algorithm_details#message-authentication-codes--macs-">Algorithm Details</seealso> + in the User's Guide. + </p> + </desc> + </func> </funcs> <section> @@ -886,75 +1079,6 @@ </func> <func> - <name name="hmac" arity="3" since="OTP R16B"/> - <name name="hmac" arity="4" since="OTP R16B"/> - <fsummary></fsummary> - <desc> - <p>Computes a HMAC of type <c>Type</c> from <c>Data</c> using - <c>Key</c> as the authentication key.</p> <p><c>MacLength</c> - will limit the size of the resultant <c>Mac</c>.</p> - </desc> - </func> - - <func> - <name name="hmac_init" arity="2" since="OTP R14B03"/> - <fsummary></fsummary> - <desc> - <p>Initializes the context for streaming HMAC operations. <c>Type</c> determines - which hash function to use in the HMAC operation. <c>Key</c> is the authentication - key. The key can be any length.</p> - </desc> - </func> - - <func> - <name name="hmac_update" arity="2" since="OTP R14B03"/> - <fsummary></fsummary> - <desc> - <p>Updates the HMAC represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c> - must have been generated using an HMAC init function (such as - <seealso marker="#hmac_init-2">hmac_init</seealso>). <c>Data</c> can be any length. <c>NewContext</c> - must be passed into the next call to <c>hmac_update</c> - or to one of the functions <seealso marker="#hmac_final-1">hmac_final</seealso> and - <seealso marker="#hmac_final_n-2">hmac_final_n</seealso> - </p> - <warning><p>Do not use a <c>Context</c> as argument in more than one - call to hmac_update or hmac_final. The semantics of reusing old contexts - in any way is undefined and could even crash the VM in earlier releases. - The reason for this limitation is a lack of support in the underlying - libcrypto API.</p></warning> - </desc> - </func> - - <func> - <name name="hmac_final" arity="1" since="OTP R14B03"/> - <fsummary></fsummary> - <desc> - <p>Finalizes the HMAC operation referenced by <c>Context</c>. The size of the resultant MAC is - determined by the type of hash function used to generate it.</p> - </desc> - </func> - - <func> - <name name="hmac_final_n" arity="2" since="OTP R14B03"/> - <fsummary></fsummary> - <desc> - <p>Finalizes the HMAC operation referenced by <c>Context</c>. <c>HashLen</c> must be greater than - zero. <c>Mac</c> will be a binary with at most <c>HashLen</c> bytes. Note that if HashLen is greater than the actual number of bytes returned from the underlying hash, the returned hash will have fewer than <c>HashLen</c> bytes.</p> - </desc> - </func> - - <func> - <name name="cmac" arity="3" since="OTP 20.0"/> - <name name="cmac" arity="4" since="OTP 20.0"/> - <fsummary>Calculates the Cipher-based Message Authentication Code.</fsummary> - <desc> - <p>Computes a CMAC of type <c>Type</c> from <c>Data</c> using - <c>Key</c> as the authentication key.</p> <p><c>MacLength</c> - will limit the size of the resultant <c>Mac</c>.</p> - </desc> - </func> - - <func> <name name="info_fips" arity="0" since="OTP 20.0"/> <fsummary>Provides information about the FIPS operating status.</fsummary> <desc> @@ -1068,15 +1192,6 @@ </func> <func> - <name name="poly1305" arity="2" since="OTP 21.1"/> - <fsummary></fsummary> - <desc> - <p>Computes a POLY1305 message authentication code (<c>Mac</c>) from <c>Data</c> using - <c>Key</c> as the authentication key.</p> - </desc> - </func> - - <func> <name name="private_decrypt" arity="4" since="OTP R16B01"/> <fsummary>Decrypts CipherText using the private Key.</fsummary> <desc> @@ -1901,7 +2016,7 @@ FloatValue = rand:uniform(). % again <seealso marker="#stream_encrypt-2">stream_encrypt</seealso> and <seealso marker="#stream_decrypt-2">stream_decrypt</seealso></p> <p>For keylengths see the - <seealso marker="crypto:algorithm_details#stream-ciphers">User's Guide</seealso>. + <seealso marker="crypto:algorithm_details#ciphers">User's Guide</seealso>. </p> </desc> </func> @@ -1917,7 +2032,7 @@ FloatValue = rand:uniform(). % again <seealso marker="#stream_encrypt-2">stream_encrypt</seealso> and <seealso marker="#stream_decrypt-2">stream_decrypt</seealso>.</p> <p>For keylengths and iv-sizes see the - <seealso marker="crypto:algorithm_details#stream-ciphers">User's Guide</seealso>. + <seealso marker="crypto:algorithm_details#ciphers">User's Guide</seealso>. </p> </desc> </func> @@ -1961,6 +2076,115 @@ FloatValue = rand:uniform(). % again </desc> </func> + <func> + <name name="hmac" arity="3" since="OTP R16B"/> + <name name="hmac" arity="4" since="OTP R16B"/> + <fsummary></fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac-4">mac/4</seealso> or + <seealso marker="crypto#macN-5">macN/5</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Computes a HMAC of type <c>Type</c> from <c>Data</c> using + <c>Key</c> as the authentication key.</p> <p><c>MacLength</c> + will limit the size of the resultant <c>Mac</c>.</p> + </desc> + </func> + + <func> + <name name="hmac_init" arity="2" since="OTP R14B03"/> + <fsummary></fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac_init-3">mac_init/3</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Initializes the context for streaming HMAC operations. <c>Type</c> determines + which hash function to use in the HMAC operation. <c>Key</c> is the authentication + key. The key can be any length.</p> + </desc> + </func> + + <func> + <name name="hmac_update" arity="2" since="OTP R14B03"/> + <fsummary></fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac_update-2">mac_update/2</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Updates the HMAC represented by <c>Context</c> using the given <c>Data</c>. <c>Context</c> + must have been generated using an HMAC init function (such as + <seealso marker="#hmac_init-2">hmac_init</seealso>). <c>Data</c> can be any length. <c>NewContext</c> + must be passed into the next call to <c>hmac_update</c> + or to one of the functions <seealso marker="#hmac_final-1">hmac_final</seealso> and + <seealso marker="#hmac_final_n-2">hmac_final_n</seealso> + </p> + <warning><p>Do not use a <c>Context</c> as argument in more than one + call to hmac_update or hmac_final. The semantics of reusing old contexts + in any way is undefined and could even crash the VM in earlier releases. + The reason for this limitation is a lack of support in the underlying + libcrypto API.</p></warning> + </desc> + </func> + + <func> + <name name="hmac_final" arity="1" since="OTP R14B03"/> + <fsummary></fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac_final-1">mac_final/1</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Finalizes the HMAC operation referenced by <c>Context</c>. The size of the resultant MAC is + determined by the type of hash function used to generate it.</p> + </desc> + </func> + + <func> + <name name="hmac_final_n" arity="2" since="OTP R14B03"/> + <fsummary></fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac_finalN-2">mac_finalN/2</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Finalizes the HMAC operation referenced by <c>Context</c>. <c>HashLen</c> must be greater than + zero. <c>Mac</c> will be a binary with at most <c>HashLen</c> bytes. Note that if HashLen is greater than the actual number of bytes returned from the underlying hash, the returned hash will have fewer than <c>HashLen</c> bytes.</p> + </desc> + </func> + + <func> + <name name="cmac" arity="3" since="OTP 20.0"/> + <name name="cmac" arity="4" since="OTP 20.0"/> + <fsummary>Calculates the Cipher-based Message Authentication Code.</fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac-4">mac/4</seealso> or + <seealso marker="crypto#macN-5">macN/5</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Computes a CMAC of type <c>Type</c> from <c>Data</c> using + <c>Key</c> as the authentication key.</p> <p><c>MacLength</c> + will limit the size of the resultant <c>Mac</c>.</p> + </desc> + </func> + + <func> + <name name="poly1305" arity="2" since="OTP 21.1"/> + <fsummary></fsummary> + <desc> + <dont><p>Don't use this function for new programs! Use + <seealso marker="crypto#mac-3">mac/3</seealso> or + <seealso marker="crypto#macN-4">macN/4</seealso> in + <seealso marker="crypto:new_api">the new api</seealso>.</p> + </dont> + <p>Computes a POLY1305 message authentication code (<c>Mac</c>) from <c>Data</c> using + <c>Key</c> as the authentication key.</p> + </desc> + </func> + </funcs> diff --git a/lib/crypto/doc/src/new_api.xml b/lib/crypto/doc/src/new_api.xml index bd2334ac9f..aacf5e4f76 100644 --- a/lib/crypto/doc/src/new_api.xml +++ b/lib/crypto/doc/src/new_api.xml @@ -40,7 +40,7 @@ to maintain. </p> <p>It turned out that using the old api in the new way (more about that later), and still keep it - backwards compatible, was not possible. Specially as more precision in the error messages was wanted + backwards compatible, was not possible. Specially as more precision in the error messages is desired it could not be combined with the old standard. </p> <p>Therefore the old api (see next section) is kept for now but internally implemented with new primitives. @@ -49,7 +49,7 @@ <section> <title>The old API</title> - <p>The old functions - not recommended for new programs - are:</p> + <p>The old functions - not recommended for new programs - are for chipers:</p> <list> <item><seealso marker="crypto#block_encrypt-3">block_encrypt/3</seealso></item> <item><seealso marker="crypto#block_encrypt-4">block_encrypt/4</seealso></item> @@ -59,61 +59,101 @@ <item><seealso marker="crypto#stream_init-2">stream_init/3</seealso></item> <item><seealso marker="crypto#stream_encrypt-2">stream_encrypt/2</seealso></item> <item><seealso marker="crypto#stream_decrypt-2">stream_decrypt/2</seealso></item> + </list> + <p>for lists of supported algorithms:</p> + <list> <item><seealso marker="crypto#supports-0">supports/0</seealso></item> </list> + <p>and for MACs (Message Authentication Codes):</p> + <list> + <item><seealso marker="crypto#cmac-3">cmac/3</seealso></item> + <item><seealso marker="crypto#cmac-4">cmac/4</seealso></item> + <item><seealso marker="crypto#hmac-3">hmac/3</seealso></item> + <item><seealso marker="crypto#hmac-4">hmac/4</seealso></item> + <item><seealso marker="crypto#hmac_init-2">hmac_init/2</seealso></item> + <item><seealso marker="crypto#hmac_update-2">hmac_update/2</seealso></item> + <item><seealso marker="crypto#hmac_final-1">hmac_final/1</seealso></item> + <item><seealso marker="crypto#hmac_final_n-2">hmac_final_n/2</seealso></item> + <item><seealso marker="crypto#poly1305-2">poly1305/2</seealso></item> + </list> <p>They are not deprecated for now, but may be in a future release. </p> </section> <section> <title>The new API</title> - <p>The new functions for encrypting or decrypting one single binary are: - </p> - <list> - <item><seealso marker="crypto#crypto_one_time/4">crypto_one_time/4</seealso></item> - <item><seealso marker="crypto#crypto_one_time/5">crypto_one_time/5</seealso></item> - <item><seealso marker="crypto#crypto_one_time_aead/6">crypto_one_time_aead/6</seealso></item> - <item><seealso marker="crypto#crypto_one_time_aead/7">crypto_one_time_aead/7</seealso></item> - </list> - <p>In those functions the internal crypto state is first created and initialized - with the cipher type, the key and possibly other data. Then the single binary is encrypted - or decrypted, - the crypto state is de-allocated and the result of the crypto operation is returned. - </p> - <p>The <c>crypto_one_time_aead</c> functions are for the ciphers of mode <c>ccm</c> or - <c>gcm</c>, and for the cipher <c>chacha20-poly1305</c>. - </p> - <p>For repeated encryption or decryption of a text divided in parts, where the internal - crypto state is initialized once, and then many binaries are encrypted or decrypted with - the same state, the functions are: - </p> - <list> - <item><seealso marker="crypto#crypto_init/4">crypto_init/4</seealso></item> - <item><seealso marker="crypto#crypto_init/3">crypto_init/3</seealso></item> - <item><seealso marker="crypto#crypto_update/2">crypto_update/2</seealso></item> - </list> - <p>The <c>crypto_init</c> initialies an internal cipher state, and one or more calls of - <c>crypto_update</c> does the acual encryption or decryption. Note that AEAD ciphers - can't be handled this way due to their nature. - </p> - <p>For repeated encryption or decryption of a text divided in parts where the - same cipher and same key is used, but a new initialization vector (nounce) should be applied - for each part, the functions are: - </p> - <list> - <item><seealso marker="crypto#crypto_dyn_iv_init/3">crypto_dyn_iv_init/3</seealso></item> - <item><seealso marker="crypto#crypto_dyn_iv_update/3">crypto_dyn_iv_update/3</seealso></item> - </list> - <p>An example of where those functions are needed, is when handling the TLS protocol.</p> - <p>For information about available algorithms, use: - </p> - <list> - <item><seealso marker="crypto#supports-1">supports/1</seealso></item> - <item><seealso marker="crypto#hash_info-1">hash_info/1</seealso></item> - <item><seealso marker="crypto#cipher_info-1">cipher_info/1</seealso></item> - </list> + <section> + <title>Encryption and decryption</title> + <p>The new functions for encrypting or decrypting one single binary are: + </p> + <list> + <item><seealso marker="crypto#crypto_one_time/4">crypto_one_time/4</seealso></item> + <item><seealso marker="crypto#crypto_one_time/5">crypto_one_time/5</seealso></item> + <item><seealso marker="crypto#crypto_one_time_aead/6">crypto_one_time_aead/6</seealso></item> + <item><seealso marker="crypto#crypto_one_time_aead/7">crypto_one_time_aead/7</seealso></item> + </list> + <p>In those functions the internal crypto state is first created and initialized + with the cipher type, the key and possibly other data. Then the single binary is encrypted + or decrypted, + the crypto state is de-allocated and the result of the crypto operation is returned. + </p> + <p>The <c>crypto_one_time_aead</c> functions are for the ciphers of mode <c>ccm</c> or + <c>gcm</c>, and for the cipher <c>chacha20-poly1305</c>. + </p> + <p>For repeated encryption or decryption of a text divided in parts, where the internal + crypto state is initialized once, and then many binaries are encrypted or decrypted with + the same state, the functions are: + </p> + <list> + <item><seealso marker="crypto#crypto_init/4">crypto_init/4</seealso></item> + <item><seealso marker="crypto#crypto_init/3">crypto_init/3</seealso></item> + <item><seealso marker="crypto#crypto_update/2">crypto_update/2</seealso></item> + </list> + <p>The <c>crypto_init</c> initialies an internal cipher state, and one or more calls of + <c>crypto_update</c> does the acual encryption or decryption. Note that AEAD ciphers + can't be handled this way due to their nature. + </p> + <p>For repeated encryption or decryption of a text divided in parts where the + same cipher and same key is used, but a new initialization vector (nounce) should be applied + for each part, the functions are: + </p> + <list> + <item><seealso marker="crypto#crypto_dyn_iv_init/3">crypto_dyn_iv_init/3</seealso></item> + <item><seealso marker="crypto#crypto_dyn_iv_update/3">crypto_dyn_iv_update/3</seealso></item> + </list> + <p>An example of where those functions are needed, is when handling the TLS protocol.</p> + <p>For information about available algorithms, use: + </p> + <list> + <item><seealso marker="crypto#supports-1">supports/1</seealso></item> + <item><seealso marker="crypto#hash_info-1">hash_info/1</seealso></item> + <item><seealso marker="crypto#cipher_info-1">cipher_info/1</seealso></item> + </list> + </section> <section> + <title>MACs (Message Authentication Codes)</title> + <p>The new functions for calculating a MAC of a single piece of text are:</p> + <list> + <item><seealso marker="crypto#mac-3">mac/3</seealso></item> + <item><seealso marker="crypto#mac-4">mac/4</seealso></item> + <item><seealso marker="crypto#macN-4">macN/4</seealso></item> + <item><seealso marker="crypto#macN-5">macN/5</seealso></item> + </list> + <p>For calculating a MAC of a text divided in parts use:</p> + <list> + <item><seealso marker="crypto#mac_init-2">mac_init/2</seealso></item> + <item><seealso marker="crypto#mac_init-3">mac_init/3</seealso></item> + <item><seealso marker="crypto#mac_update-2">mac_update/2</seealso></item> + <item><seealso marker="crypto#mac_final-1">mac_final/1</seealso></item> + <item><seealso marker="crypto#mac_finalN-2">mac_finalN/2</seealso></item> + </list> + </section> + </section> + + <section> + <title>Examples of the new api</title> + <section> <title>Examples of crypto_init/4 and crypto_update/2</title> <p>The functions <seealso marker="crypto#crypto_init/4">crypto_init/4</seealso> and <seealso marker="crypto#crypto_update/2">crypto_update/2</seealso> are intended @@ -143,7 +183,7 @@ 8> crypto:crypto_update(StateDec, <<67,44,216,166,25,130,203>>). <<"First b">> 9> crypto:crypto_update(StateDec, <<5,66,6,162,16,79,94,115,234,197, - 94,253,16,144,151>>). + 94,253,16,144,151>>). <<"ytesSecond byte">> 10> crypto:crypto_update(StateDec, <<41>>). <<"s">> @@ -159,16 +199,16 @@ </p> <code type="erl"> encode(Crypto, Key, IV) -> - crypto_loop(crypto:crypto_init(Crypto, Key, IV, true)). + crypto_loop(crypto:crypto_init(Crypto, Key, IV, true)). crypto_loop(State) -> - receive - {Text, Requester} -> - Requester ! crypto:crypto_update(State, Text), - loop(State) - end. + receive + {Text, Requester} -> + Requester ! crypto:crypto_update(State, Text), + loop(State) + end. </code> - </section> + </section> <section> <title>Example of crypto_one_time/5</title> @@ -219,6 +259,35 @@ </p> </section> + <section> + <title>Example of mac_init mac_update and mac_final</title> + <code> + 1> Key = <<1:128>>. + <<0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1>> + 2> StateMac = crypto:mac_init(cmac, aes_128_cbc, Key). + #Ref<0.2424664121.2781478916.232610> + 3> crypto:mac_update(StateMac, <<"First bytes">>). + #Ref<0.2424664121.2781478916.232610> + 4> crypto:mac_update(StateMac, " "). + #Ref<0.2424664121.2781478916.232610> + 5> crypto:mac_update(StateMac, <<"last bytes">>). + #Ref<0.2424664121.2781478916.232610> + 6> crypto:mac_final(StateMac). + <<68,191,219,128,84,77,11,193,197,238,107,6,214,141,160, + 249>> + 7> + </code> + <p>and compare the result with a single calculation just for this example:</p> + <code> + 7> crypto:mac(cmac, aes_128_cbc, Key, "First bytes last bytes"). + <<68,191,219,128,84,77,11,193,197,238,107,6,214,141,160, + 249>> + 8> v(7) == v(6). + true + 9> + </code> + </section> + </section> <section> @@ -233,7 +302,7 @@ on the mode. An example is the ccm mode which has a variant called ccm8 where the so called tag has a length of eight bits. </p> - <p>The old names had by time lost any common naming which the new names now introduces. The new names include + <p>The old names had by time lost any common naming convention which the new names now introduces. The new names include the key length which improves the error checking in the lower levels of the crypto application. </p> diff --git a/lib/crypto/src/crypto.erl b/lib/crypto/src/crypto.erl index 8ffdde2b90..965697578d 100644 --- a/lib/crypto/src/crypto.erl +++ b/lib/crypto/src/crypto.erl @@ -28,9 +28,6 @@ -export([hash/2, hash_init/1, hash_update/2, hash_final/1]). -export([sign/4, sign/5, verify/5, verify/6]). -export([generate_key/2, generate_key/3, compute_key/4]). --export([hmac/3, hmac/4, hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]). --export([cmac/3, cmac/4]). --export([poly1305/2]). -export([exor/2, strong_rand_bytes/1, mod_pow/3]). -export([rand_seed/0, rand_seed_alg/1, rand_seed_alg/2]). -export([rand_seed_s/0, rand_seed_alg_s/1, rand_seed_alg_s/2]). @@ -48,6 +45,9 @@ -export([rand_seed/1]). %% Old interface. Now implemented with the New interface +-export([hmac/3, hmac/4, hmac_init/2, hmac_update/2, hmac_final/1, hmac_final_n/2]). +-export([cmac/3, cmac/4]). +-export([poly1305/2]). -export([stream_init/2, stream_init/3, stream_encrypt/2, stream_decrypt/2, @@ -62,7 +62,9 @@ crypto_one_time_aead/6, crypto_one_time_aead/7, crypto_dyn_iv_init/3, crypto_dyn_iv_update/3, - supports/1 + supports/1, + mac/3, mac/4, macN/4, macN/5, + mac_init/2, mac_init/3, mac_update/2, mac_final/1, mac_finalN/2 ]). @@ -109,9 +111,10 @@ stream_state/0, hmac_state/0, hash_state/0, - crypto_state/0 + crypto_state/0, + mac_state/0 ]). - + %% Private. For tests. -export([packed_openssl_version/4, engine_methods_convert_to_bitmask/2, get_test_engine/0]). @@ -136,7 +139,7 @@ -type rsa_private() :: [key_integer()] . % [E, N, D] | [E, N, D, P1, P2, E1, E2, C] -type rsa_params() :: {ModulusSizeInBits::integer(), PublicExponent::key_integer()} . --type dss_public() :: [key_integer()] . % [P, Q, G, Y] +-type dss_public() :: [key_integer()] . % [P, Q, G, Y] -type dss_private() :: [key_integer()] . % [P, Q, G, X] -type ecdsa_public() :: key_integer() . @@ -282,7 +285,7 @@ %%% New cipher schema %%% -type cipher() :: cipher_no_iv() - | cipher_iv() + | cipher_iv() | cipher_aead() . -type cipher_no_iv() :: aes_128_ecb @@ -326,7 +329,7 @@ -type cipher_aead() :: aes_128_ccm | aes_192_ccm | aes_256_ccm - + | aes_128_gcm | aes_192_gcm | aes_256_gcm @@ -334,23 +337,6 @@ | chacha20_poly1305 . -%% -type retired_cipher_no_iv_aliases() :: aes_ecb . - -%% -type retired_cipher_iv_aliases() :: aes_cbc -%% | aes_cbc128 % aes_128_cbc -%% | aes_cbc256 % aes_256_cbc -%% | aes_cfb128 -%% | aes_cfb8 -%% | aes_ctr -%% | des3_cbc % des_ede3_cbc -%% | des_ede3 % des_ede3_cbc -%% | des_ede3_cbf % des_ede3_cfb -%% | des3_cbf % des_ede3_cfb -%% | des3_cfb . % des_ede3_cfb - -%% -type retired_cipher_aead_aliases() :: aes_ccm -%% | aes_gcm . - %%%---------------------------------------------------------------- %%% Old cipher scheme %%% @@ -365,7 +351,7 @@ -type stream_cipher() :: ctr_cipher() | chacha20 | rc4 . - + %%%---- -type cbc_cipher() :: aes_128_cbc @@ -374,7 +360,7 @@ | blowfish_cbc | des_cbc | des_ede3_cbc - | rc2_cbc + | rc2_cbc | retired_cbc_cipher_aliases() . -type retired_cbc_cipher_aliases() :: aes_cbc % aes_*_cbc @@ -382,7 +368,7 @@ | aes_cbc256 % aes_256_cbc | des3_cbc % des_ede3_cbc | des_ede3 . % des_ede3_cbc - + %%%---- -type cfb_cipher() :: aes_128_cfb128 | aes_192_cfb128 @@ -398,7 +384,7 @@ -type retired_cfb_cipher_aliases() :: aes_cfb8 % aes_*_cfb8 | aes_cfb128 % aes_*_cfb128 | des3_cbf % des_ede3_cfb, cfb misspelled - | des3_cfb % des_ede3_cfb + | des3_cfb % des_ede3_cfb | des_ede3_cbf .% cfb misspelled @@ -457,6 +443,19 @@ %%-------------------------------------------------------------------- +%% +%% Make the new descriptive_error() look like the old run_time_error() +%% +-define(COMPAT(CALL), + try begin CALL end + catch + error:{error, {_File,_Line}, _Reason} -> + error(badarg); + error:{E, {_File,_Line}, _Reason} when E==notsup ; E==badarg -> + error(E) + end). + +%%-------------------------------------------------------------------- -compile(no_native). -on_load(on_load/0). -define(CRYPTO_NIF_VSN,302). @@ -580,7 +579,7 @@ hash(Type, Data) -> -spec hash_init(Type) -> State when Type :: hash_algorithm(), State :: hash_state(). -hash_init(Type) -> +hash_init(Type) -> notsup_to_error(hash_init_nif(Type)). -spec hash_update(State, Data) -> NewState when State :: hash_state(), @@ -599,25 +598,139 @@ hash_final(Context) -> %%%================================================================ %%% %%% MACs (Message Authentication Codes) -%%% +%%% %%%================================================================ -%%%---- HMAC - -type hmac_hash_algorithm() :: sha1() | sha2() | sha3() | compatibility_only_hash(). -%%%---- hmac/3,4 +-type cmac_cipher_algorithm() :: aes_128_cbc | aes_192_cbc | aes_256_cbc | blowfish_cbc + | des_cbc | des_ede3_cbc | rc2_cbc + | aes_128_cfb128 | aes_192_cfb128 | aes_256_cfb128 + | aes_128_cfb8 | aes_192_cfb8 | aes_256_cfb8 + . + +%%%---------------------------------------------------------------- +%%% Calculate MAC for the whole text at once + +-spec mac(Type :: poly1305, Key, Data) -> Mac | descriptive_error() + when Key :: iodata(), + Data :: iodata(), + Mac :: binary(). + +mac(poly1305, Key, Data) -> mac(poly1305, undefined, Key, Data). + + +-spec mac(Type, SubType, Key, Data) -> Mac | descriptive_error() + when Type :: hmac | cmac | poly1305, + SubType :: hmac_hash_algorithm() | cmac_cipher_algorithm() | undefined, + Key :: iodata(), + Data :: iodata(), + Mac :: binary(). + +mac(Type, SubType, Key, Data) -> mac_nif(Type, SubType, Key, Data). + + + +-spec macN(Type :: poly1305, Key, Data, MacLength) -> Mac | descriptive_error() + when Key :: iodata(), + Data :: iodata(), + Mac :: binary(), + MacLength :: pos_integer(). + +macN(Type, Key, Data, MacLength) -> + macN(Type, undefined, Key, Data, MacLength). + + +-spec macN(Type, SubType, Key, Data, MacLength) -> Mac | descriptive_error() + when Type :: hmac | cmac | poly1305, + SubType :: hmac_hash_algorithm() | cmac_cipher_algorithm() | undefined, + Key :: iodata(), + Data :: iodata(), + Mac :: binary(), + MacLength :: pos_integer(). + +macN(Type, SubType, Key, Data, MacLength) -> + erlang:binary_part(mac(Type,SubType,Key,Data), 0, MacLength). + + +%%%---------------------------------------------------------------- +%%% Calculate the MAC by uppdating by pieces of the text + +-opaque mac_state() :: reference() . + +-spec mac_init(Type :: poly1305, Key) -> State | descriptive_error() + when Key :: iodata(), + State :: mac_state() . +mac_init(poly1305, Key) -> + mac_init_nif(poly1305, undefined, Key). + + +-spec mac_init(Type, SubType, Key) -> State | descriptive_error() + when Type :: hmac | cmac | poly1305, + SubType :: hmac_hash_algorithm() | cmac_cipher_algorithm() | undefined, + Key :: iodata(), + State :: mac_state() . +mac_init(Type, SubType, Key) -> + mac_init_nif(Type, SubType, Key). + + +-spec mac_update(State0, Data) -> State | descriptive_error() + when Data :: iodata(), + State0 :: mac_state(), + State :: mac_state(). +mac_update(Ref, Data) -> + mac_update_nif(Ref, Data). + + + +-spec mac_final(State) -> Mac | descriptive_error() + when State :: mac_state(), + Mac :: binary(). +mac_final(Ref) -> + mac_final_nif(Ref). + + +-spec mac_finalN(State, MacLength) -> Mac | descriptive_error() + when State :: mac_state(), + MacLength :: pos_integer(), + Mac :: binary(). +mac_finalN(Ref, MacLength) -> + erlang:binary_part(mac_final(Ref), 0, MacLength). + --spec hmac(Type, Key, Data) -> +%%%---------------------------------------------------------------- +%%% NIFs for the functions above + +mac_nif(_Type, _SubType, _Key, _Data) -> ?nif_stub. + +mac_init_nif(_Type, _SubType, _Key) -> ?nif_stub. +mac_update_nif(_Ref, _Data) -> ?nif_stub. +mac_final_nif(_Ref) -> ?nif_stub. + +%%%================================================================ +%%% +%%% The "Old API", kept for compatibility +%%% +%%%================================================================ + +%%%---------------------------------------------------------------- +%%%---------------------------------------------------------------- +%%% Message Authentication Codes, MAC +%%% + +%%%---- HMAC + +%%%---- hmac/3,4 + +-spec hmac(Type, Key, Data) -> Mac when Type :: hmac_hash_algorithm(), Key :: iodata(), Data :: iodata(), Mac :: binary() . hmac(Type, Key, Data) -> - Data1 = iolist_to_binary(Data), - hmac(Type, Key, Data1, undefined, erlang:byte_size(Data1), max_bytes()). + ?COMPAT(mac(hmac, Type, Key, Data)). --spec hmac(Type, Key, Data, MacLength) -> +-spec hmac(Type, Key, Data, MacLength) -> Mac when Type :: hmac_hash_algorithm(), Key :: iodata(), Data :: iodata(), @@ -625,45 +738,43 @@ hmac(Type, Key, Data) -> Mac :: binary() . hmac(Type, Key, Data, MacLength) -> - Data1 = iolist_to_binary(Data), - hmac(Type, Key, Data1, MacLength, erlang:byte_size(Data1), max_bytes()). + ?COMPAT(macN(hmac, Type, Key, Data, MacLength)). %%%---- hmac_init, hamc_update, hmac_final --opaque hmac_state() :: binary(). +-opaque hmac_state() :: mac_state(). % Was: binary(). -spec hmac_init(Type, Key) -> State when Type :: hmac_hash_algorithm(), Key :: iodata(), State :: hmac_state() . hmac_init(Type, Key) -> - notsup_to_error(hmac_init_nif(Type, Key)). + ?COMPAT(mac_init(hmac, Type, Key)). %%%---- hmac_update -spec hmac_update(State, Data) -> NewState when Data :: iodata(), State :: hmac_state(), NewState :: hmac_state(). -hmac_update(State, Data0) -> - Data = iolist_to_binary(Data0), - hmac_update(State, Data, erlang:byte_size(Data), max_bytes()). +hmac_update(State, Data) -> + ?COMPAT(mac_update(State, Data)). %%%---- hmac_final -spec hmac_final(State) -> Mac when State :: hmac_state(), Mac :: binary(). hmac_final(Context) -> - notsup_to_error(hmac_final_nif(Context)). + ?COMPAT(mac_final(Context)). -spec hmac_final_n(State, HashLen) -> Mac when State :: hmac_state(), HashLen :: integer(), Mac :: binary(). hmac_final_n(Context, HashLen) -> - notsup_to_error(hmac_final_nif(Context, HashLen)). + ?COMPAT(mac_finalN(Context, HashLen)). %%%---- CMAC --define(CMAC_CIPHER_ALGORITHM, cbc_cipher() | cfb_cipher() | blowfish_cbc | des_ede3 | rc2_cbc ). +-define(CMAC_CIPHER_ALGORITHM, cbc_cipher() | cfb_cipher() | blowfish_cbc | des_ede3 | rc2_cbc ). -spec cmac(Type, Key, Data) -> Mac when Type :: ?CMAC_CIPHER_ALGORITHM, @@ -671,42 +782,31 @@ hmac_final_n(Context, HashLen) -> Data :: iodata(), Mac :: binary(). cmac(Type, Key, Data) -> - notsup_to_error(cmac_nif(alias(Type), Key, Data)). + ?COMPAT(mac(cmac, alias(Type), Key, Data)). -spec cmac(Type, Key, Data, MacLength) -> Mac when Type :: ?CMAC_CIPHER_ALGORITHM, Key :: iodata(), Data :: iodata(), - MacLength :: integer(), + MacLength :: integer(), Mac :: binary(). cmac(Type, Key, Data, MacLength) -> - erlang:binary_part(cmac(alias(Type), Key, Data), 0, MacLength). + ?COMPAT(macN(cmac, alias(Type), Key, Data, MacLength)). %%%---- POLY1305 -spec poly1305(iodata(), iodata()) -> Mac when Mac :: binary(). poly1305(Key, Data) -> - poly1305_nif(Key, Data). + ?COMPAT(mac(poly1305, Key, Data)). -%%%================================================================ -%%% -%%% Encrypt/decrypt, The "Old API" -%%% -%%%================================================================ +%%%---------------------------------------------------------------- +%%%---------------------------------------------------------------- +%%% Ciphers --define(COMPAT(CALL), - try begin CALL end - catch - error:{error, {_File,_Line}, _Reason} -> - error(badarg); - error:{E, {_File,_Line}, _Reason} when E==notsup ; E==badarg -> - error(E) - end). %%%---- Cipher info -%%%---------------------------------------------------------------- -spec cipher_info(Type) -> Result | run_time_error() when Type :: cipher(), Result :: #{key_length := integer(), @@ -845,7 +945,7 @@ block_decrypt(Type, Key0, CryptoText) -> Key :: iodata(), IVec ::binary(), State :: stream_state() . -stream_init(Type, Key0, IVec) when is_binary(IVec) -> +stream_init(Type, Key0, IVec) when is_binary(IVec) -> Key = iolist_to_binary(Key0), Ref = ?COMPAT(ng_crypto_init_nif(alias(Type,Key), Key, iolist_to_binary(IVec), @@ -933,7 +1033,7 @@ next_iv(Type, Data, _Ivec) -> %%%---------------------------------------------------------------- %%% %%% Create and initialize a new state for encryption or decryption -%%% +%%% -spec crypto_init(Cipher, Key, EncryptFlag) -> State | descriptive_error() when Cipher :: cipher_no_iv(), @@ -971,12 +1071,12 @@ crypto_dyn_iv_init(Cipher, Key, EncryptFlag) -> %%% Encrypt/decrypt a sequence of bytes. The sum of the sizes %%% of all blocks must be an integer multiple of the crypto's %%% blocksize. -%%% +%%% -spec crypto_update(State, Data) -> Result | descriptive_error() - when State :: crypto_state(), - Data :: iodata(), - Result :: binary() . + when State :: crypto_state(), + Data :: iodata(), + Result :: binary() . crypto_update(State, Data0) -> case iolist_to_binary(Data0) of <<>> -> @@ -1005,7 +1105,7 @@ crypto_dyn_iv_update(State, Data0, IV) -> %%% %%% Encrypt/decrypt one set bytes. %%% The size must be an integer multiple of the crypto's blocksize. -%%% +%%% -spec crypto_one_time(Cipher, Key, Data, EncryptFlag) -> Result | descriptive_error() @@ -1015,8 +1115,15 @@ crypto_dyn_iv_update(State, Data0, IV) -> EncryptFlag :: boolean(), Result :: binary() . -crypto_one_time(Cipher, Key, Data, EncryptFlag) -> - crypto_one_time(Cipher, Key, <<>>, Data, EncryptFlag). +crypto_one_time(Cipher, Key, Data0, EncryptFlag) -> + case iolist_to_binary(Data0) of + <<>> -> + <<>>; % Known to fail on OpenSSL 0.9.8h + Data -> + ng_crypto_one_time_nif(Cipher, + iolist_to_binary(Key), <<>>, Data, + EncryptFlag) + end. -spec crypto_one_time(Cipher, Key, IV, Data, EncryptFlag) -> Result | descriptive_error() @@ -1121,7 +1228,7 @@ ng_crypto_one_time_nif(_Cipher, _Key, _IVec, _Data, _EncryptFlg) -> ?nif_stub. false -> Ciphers end). - + prepend_old_aliases(L0) -> L1 = ?if_also(des_ede3_cbc, L0, @@ -1465,7 +1572,7 @@ rand_seed_nif(_Seed) -> ?nif_stub. %%% Sign -spec sign(Algorithm, DigestType, Msg, Key) - -> Signature + -> Signature when Algorithm :: pk_sign_verify_algs(), DigestType :: rsa_digest_type() | dss_digest_type() @@ -1483,7 +1590,7 @@ sign(Algorithm, Type, Data, Key) -> -spec sign(Algorithm, DigestType, Msg, Key, Options) - -> Signature + -> Signature when Algorithm :: pk_sign_verify_algs(), DigestType :: rsa_digest_type() | dss_digest_type() @@ -1580,7 +1687,7 @@ sign_verify_compatibility(Algorithm0, Type0, _Digest) -> | rsa_x931_padding | rsa_no_padding. --type rsa_opt() :: {rsa_padding, rsa_padding()} +-type rsa_opt() :: {rsa_padding, rsa_padding()} | {signature_md, atom()} | {rsa_mgf1_md, sha} | {rsa_oaep_label, binary()} @@ -1653,7 +1760,7 @@ pkey_crypt_nif(_Algorithm, _In, _Key, _Options, _IsPrivate, _IsEncrypt) -> ?nif_ %%%================================================================ -spec generate_key(Type, Params) - -> {PublicKey, PrivKeyOut} + -> {PublicKey, PrivKeyOut} when Type :: dh | ecdh | rsa | srp, PublicKey :: dh_public() | ecdh_public() | rsa_public() | srp_public(), PrivKeyOut :: dh_private() | ecdh_private() | rsa_private() | {srp_public(),srp_private()}, @@ -1663,7 +1770,7 @@ generate_key(Type, Params) -> generate_key(Type, Params, undefined). -spec generate_key(Type, Params, PrivKeyIn) - -> {PublicKey, PrivKeyOut} + -> {PublicKey, PrivKeyOut} when Type :: dh | ecdh | rsa | srp, PublicKey :: dh_public() | ecdh_public() | rsa_public() | srp_public(), PrivKeyIn :: undefined | dh_private() | ecdh_private() | rsa_private() | {srp_public(),srp_private()}, @@ -1814,7 +1921,7 @@ mod_pow(Base, Exponent, Prime) -> %%%====================================================================== %%% %%% Engine functions -%%% +%%% %%%====================================================================== %%%---- Refering to keys stored in an engine: @@ -2121,7 +2228,7 @@ ensure_engine_unloaded(Engine) -> %%---------------------------------------------------------------------- %% Function: ensure_engine_unloaded/2 %%---------------------------------------------------------------------- --spec ensure_engine_unloaded(Engine, EngineMethods) -> +-spec ensure_engine_unloaded(Engine, EngineMethods) -> Result when Engine :: engine_ref(), EngineMethods :: [engine_method_type()], Result :: ok | {error, Reason::term()}. @@ -2203,7 +2310,7 @@ path2bin(Path) when is_list(Path) -> %%%================================================================ %%% %%% Internal functions -%%% +%%% %%%================================================================ max_bytes() -> @@ -2235,43 +2342,6 @@ hash_init_nif(_Hash) -> ?nif_stub. hash_update_nif(_State, _Data) -> ?nif_stub. hash_final_nif(_State) -> ?nif_stub. -%% HMAC -------------------------------------------------------------------- - -hmac(Type, Key, Data, MacSize, Size, MaxBytes) when Size =< MaxBytes -> - notsup_to_error( - case MacSize of - undefined -> hmac_nif(Type, Key, Data); - _ -> hmac_nif(Type, Key, Data, MacSize) - end); -hmac(Type, Key, Data, MacSize, Size, MaxBytes) -> - State0 = hmac_init(Type, Key), - State1 = hmac_update(State0, Data, Size, MaxBytes), - case MacSize of - undefined -> hmac_final(State1); - _ -> hmac_final_n(State1, MacSize) - end. - -hmac_update(State, Data, Size, MaxBytes) when Size =< MaxBytes -> - notsup_to_error(hmac_update_nif(State, Data)); -hmac_update(State0, Data, _, MaxBytes) -> - <<Increment:MaxBytes/binary, Rest/binary>> = Data, - State = notsup_to_error(hmac_update_nif(State0, Increment)), - hmac_update(State, Rest, erlang:byte_size(Rest), MaxBytes). - -hmac_nif(_Type, _Key, _Data) -> ?nif_stub. -hmac_nif(_Type, _Key, _Data, _MacSize) -> ?nif_stub. -hmac_init_nif(_Type, _Key) -> ?nif_stub. -hmac_update_nif(_Context, _Data) -> ?nif_stub. -hmac_final_nif(_Context) -> ?nif_stub. -hmac_final_nif(_Context, _MacSize) -> ?nif_stub. - -%% CMAC -cmac_nif(_Type, _Key, _Data) -> ?nif_stub. - -%% POLY1305 -poly1305_nif(_Key, _Data) -> ?nif_stub. - - %% CIPHERS -------------------------------------------------------------------- cipher_info_nif(_Type) -> ?nif_stub. diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 56691223c4..0da70d5592 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -176,19 +176,19 @@ groups() -> ]}, {md4, [], [hash]}, - {md5, [], [hash, hmac]}, + {md5, [], [hash, hmac, hmac_update]}, {ripemd160, [], [hash]}, - {sha, [], [hash, hmac]}, - {sha224, [], [hash, hmac]}, - {sha256, [], [hash, hmac]}, - {sha384, [], [hash, hmac]}, - {sha512, [], [hash, hmac]}, - {sha3_224, [], [hash, hmac]}, - {sha3_256, [], [hash, hmac]}, - {sha3_384, [], [hash, hmac]}, - {sha3_512, [], [hash, hmac]}, - {blake2b, [], [hash, hmac]}, - {blake2s, [], [hash, hmac]}, + {sha, [], [hash, hmac, hmac_update]}, + {sha224, [], [hash, hmac, hmac_update]}, + {sha256, [], [hash, hmac, hmac_update]}, + {sha384, [], [hash, hmac, hmac_update]}, + {sha512, [], [hash, hmac, hmac_update]}, + {sha3_224, [], [hash, hmac, hmac_update]}, + {sha3_256, [], [hash, hmac, hmac_update]}, + {sha3_384, [], [hash, hmac, hmac_update]}, + {sha3_512, [], [hash, hmac, hmac_update]}, + {blake2b, [], [hash, hmac, hmac_update]}, + {blake2s, [], [hash, hmac, hmac_update]}, {no_blake2b, [], [no_hash, no_hmac]}, {no_blake2s, [], [no_hash, no_hmac]}, {rsa, [], [sign_verify, @@ -265,9 +265,9 @@ groups() -> %% New cipher nameing schema {des_ede3_cbc, [], [api_ng, api_ng_one_shot, api_ng_tls]}, {des_ede3_cfb, [], [api_ng, api_ng_one_shot, api_ng_tls]}, - {aes_128_cbc, [], [api_ng, api_ng_one_shot, api_ng_tls]}, + {aes_128_cbc, [], [api_ng, api_ng_one_shot, api_ng_tls, cmac]}, {aes_192_cbc, [], [api_ng, api_ng_one_shot, api_ng_tls]}, - {aes_256_cbc, [], [api_ng, api_ng_one_shot, api_ng_tls]}, + {aes_256_cbc, [], [api_ng, api_ng_one_shot, api_ng_tls, cmac]}, {aes_128_ctr, [], [api_ng, api_ng_one_shot, api_ng_tls]}, {aes_192_ctr, [], [api_ng, api_ng_one_shot, api_ng_tls]}, {aes_256_ctr, [], [api_ng, api_ng_one_shot, api_ng_tls]}, @@ -386,7 +386,7 @@ init_per_testcase(info, Config) -> init_per_testcase(cmac, Config) -> case is_supported(cmac) of true -> - Config; + configure_mac(cmac, proplists:get_value(type,Config), Config); false -> {skip, "CMAC is not supported"} end; @@ -405,6 +405,8 @@ init_per_testcase(generate, Config) -> end; _ -> Config end; +init_per_testcase(hmac, Config) -> + configure_mac(hmac, proplists:get_value(type,Config), Config); init_per_testcase(_Name,Config) -> Config. @@ -452,27 +454,41 @@ no_hash(Config) when is_list(Config) -> notsup(fun crypto:hash_init/1, [Type]). %%-------------------------------------------------------------------- hmac() -> - [{doc, "Test all different hmac functions"}]. + [{doc, "Test hmac function"}]. hmac(Config) when is_list(Config) -> - {Type, Keys, DataLE, Expected} = proplists:get_value(hmac, Config), - Data = lazy_eval(DataLE), - hmac(Type, Keys, Data, Expected), - hmac(Type, lists:map(fun iolistify/1, Keys), lists:map(fun iolistify/1, Data), Expected), - hmac_increment(Type). + Tuples = lazy_eval(proplists:get_value(hmac, Config)), + lists:foreach(fun hmac_check/1, Tuples), + lists:foreach(fun hmac_check/1, mac_listify(Tuples)). + %%-------------------------------------------------------------------- no_hmac() -> [{doc, "Test all disabled hmac functions"}]. no_hmac(Config) when is_list(Config) -> Type = ?config(type, Config), - notsup(fun crypto:hmac/3, [Type, <<"Key">>, <<"Hi There">>]), + notsup(fun crypto:hmac/3, [Type, <<"Key">>, <<"Hi There">>]). + +%%-------------------------------------------------------------------- +hmac_update() -> + [{doc, "Test all incremental hmac functions"}]. +hmac_update(Config) -> + Type = ?config(type, Config), + hmac_increment(Type). + +%%-------------------------------------------------------------------- +no_hmac_update() -> + [{doc, "Test all disabled incremental hmac functions"}]. +no_hmac_update(Config) -> + Type = ?config(type, Config), notsup(fun crypto:hmac_init/2, [Type, <<"Key">>]). + %%-------------------------------------------------------------------- cmac() -> [{doc, "Test all different cmac functions"}]. cmac(Config) when is_list(Config) -> Pairs = lazy_eval(proplists:get_value(cmac, Config)), lists:foreach(fun cmac_check/1, Pairs), - lists:foreach(fun cmac_check/1, cmac_iolistify(Pairs)). + lists:foreach(fun cmac_check/1, mac_listify(Pairs)). + %%-------------------------------------------------------------------- poly1305() -> [{doc, "Test poly1305 function"}]. @@ -957,33 +973,46 @@ hash_increment(State0, [Increment | Rest]) -> State = crypto:hash_update(State0, Increment), hash_increment(State, Rest). -hmac(_, [],[],[]) -> - ok; -hmac(sha = Type, [Key | Keys], [ <<"Test With Truncation">> = Data| Rest], [Expected | Expects]) -> - call_crypto_hmac([Type, Key, Data, 20], Type, Expected), - hmac(Type, Keys, Rest, Expects); -hmac(Type, [Key | Keys], [ <<"Test With Truncation">> = Data| Rest], [Expected | Expects]) -> - call_crypto_hmac([Type, Key, Data, 16], Type, Expected), - hmac(Type, Keys, Rest, Expects); -hmac(Type, [Key | Keys], [Data| Rest], [Expected | Expects]) -> - call_crypto_hmac([Type, Key, Data], Type, Expected), - hmac(Type, Keys, Rest, Expects). - -call_crypto_hmac(Args, Type, Expected) -> - try apply(crypto, hmac, Args) + +%%%---------------------------------------------------------------- +hmac_check({hmac, sha=Type, Key, <<"Test With Truncation">>=Data, Expected}) -> + do_hmac_check(Type, Key, Data, 20, Expected); +hmac_check({hmac, Type, Key, <<"Test With Truncation">>=Data, Expected}) -> + do_hmac_check(Type, Key, Data, 16, Expected); +hmac_check({hmac, Type, Key, Data, Expected}) -> + do_hmac_check(Type, Key, Data, Expected). + + +do_hmac_check(Type, Key, Data, Expected) -> + try crypto:hmac(Type, Key, Data) of Expected -> ok; Other -> - ct:fail({{crypto,hmac,Args}, {expected,Expected}, {got,Other}}) + ct:fail({{crypto,hmac,[Type,Key,Data]}, {expected,Expected}, {got,Other}}) catch error:notsup -> ct:fail("HMAC ~p not supported", [Type]); Class:Cause -> - ct:fail({{crypto,hmac,Args}, {expected,Expected}, {got,{Class,Cause}}}) + ct:fail({{crypto,hmac,[Type,Key,Data]}, {expected,Expected}, {got,{Class,Cause}}}) end. +do_hmac_check(Type, Key, Data, MacLength, Expected) -> + try crypto:hmac(Type, Key, Data, MacLength) + of + Expected -> + ok; + Other -> + ct:fail({{crypto,hmac,[Type,Key,Data,MacLength]}, {expected,Expected}, {got,Other}}) + catch + error:notsup -> + ct:fail("HMAC ~p not supported", [Type]); + Class:Cause -> + ct:fail({{crypto,hmac,[Type,Key,Data,MacLength]}, {expected,Expected}, {got,{Class,Cause}}}) + end. + +%%%---------------------------------------------------------------- hmac_increment(Type) -> Key = hmac_key(Type), Increments = hmac_inc(Type), @@ -1002,7 +1031,8 @@ hmac_increment(State0, [Increment | Rest]) -> State = crypto:hmac_update(State0, Increment), hmac_increment(State, Rest). -cmac_check({Type, Key, Text, CMac}) -> +%%%---------------------------------------------------------------- +cmac_check({cmac, Type, Key, Text, CMac}) -> ExpCMac = iolist_to_binary(CMac), case crypto:cmac(Type, Key, Text) of ExpCMac -> @@ -1010,7 +1040,7 @@ cmac_check({Type, Key, Text, CMac}) -> Other -> ct:fail({{crypto, cmac, [Type, Key, Text]}, {expected, ExpCMac}, {got, Other}}) end; -cmac_check({Type, Key, Text, Size, CMac}) -> +cmac_check({cmac, Type, Key, Text, Size, CMac}) -> ExpCMac = iolist_to_binary(CMac), case crypto:cmac(Type, Key, Text, Size) of ExpCMac -> @@ -1020,6 +1050,24 @@ cmac_check({Type, Key, Text, Size, CMac}) -> end. +mac_check({MacType, SubType, Key, Text, Mac}) -> + ExpMac = iolist_to_binary(Mac), + case crypto:mac(MacType, SubType, Key, Text) of + ExpMac -> + ok; + Other -> + ct:fail({{crypto, mac, [MacType, SubType, Key, Text]}, {expected, ExpMac}, {got, Other}}) + end; +mac_check({MacType, SubType, Key, Text, Size, Mac}) -> + ExpMac = iolist_to_binary(Mac), + case crypto:mac(MacType, SubType, Key, Text, Size) of + ExpMac -> + ok; + Other -> + ct:fail({{crypto, mac, [MacType, SubType, Key, Text]}, {expected, ExpMac}, {got, Other}}) + end. + + block_cipher({Type, Key, PlainText}) -> Plain = iolist_to_binary(PlainText), CipherText = crypto:block_encrypt(Type, Key, PlainText), @@ -1450,17 +1498,17 @@ decstr2int(S) -> is_supported(Group) -> lists:member(Group, lists:append([Algo || {_, Algo} <- crypto:supports()])). -cmac_iolistify(Blocks) -> - lists:map(fun do_cmac_iolistify/1, Blocks). +mac_listify(Blocks) -> + lists:map(fun do_mac_listify/1, Blocks). block_iolistify(Blocks) -> lists:map(fun do_block_iolistify/1, Blocks). stream_iolistify(Streams) -> lists:map(fun do_stream_iolistify/1, Streams). -do_cmac_iolistify({Type, Key, Text, CMac}) -> - {Type, iolistify(Key), iolistify(Text), CMac}; -do_cmac_iolistify({Type, Key, Text, Size, CMac}) -> - {Type, iolistify(Key), iolistify(Text), Size, CMac}. +do_mac_listify({MType, Type, Key, Text, CMac}) -> + {MType, Type, iolistify(Key), iolistify(Text), CMac}; +do_mac_listify({MType, Type, Key, Text, Size, CMac}) -> + {MType, Type, iolistify(Key), iolistify(Text), Size, CMac}. do_stream_iolistify({Type, Key, PlainText}) -> {Type, iolistify(Key), iolistify(PlainText)}; @@ -1694,10 +1742,7 @@ group_config(md4 = Type, Config) -> group_config(md5 = Type, Config) -> Msgs = rfc_1321_msgs(), Digests = rfc_1321_md5_digests(), - Keys = rfc_2202_md5_keys() ++ [long_hmac_key(md5)], - Data = rfc_2202_msgs() ++ [long_msg()], - Hmac = rfc_2202_hmac_md5() ++ [long_hmac(md5)], - [{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(ripemd160 = Type, Config) -> Msgs = ripemd160_msgs(), Digests = ripemd160_digests(), @@ -1705,56 +1750,41 @@ group_config(ripemd160 = Type, Config) -> group_config(sha = Type, Config) -> Msgs = [rfc_4634_test1(), rfc_4634_test2_1(),long_msg()], Digests = rfc_4634_sha_digests() ++ [long_sha_digest()], - Keys = rfc_2202_sha_keys() ++ [long_hmac_key(sha)], - Data = rfc_2202_msgs() ++ [long_msg()], - Hmac = rfc_2202_hmac_sha() ++ [long_hmac(sha)], - [{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha224 = Type, Config) -> Msgs = [rfc_4634_test1(), rfc_4634_test2_1()], Digests = rfc_4634_sha224_digests(), - Keys = rfc_4231_keys(), - Data = rfc_4231_msgs(), - Hmac = rfc4231_hmac_sha224(), - [{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha256 = Type, Config) -> Msgs = [rfc_4634_test1(), rfc_4634_test2_1(), long_msg()], Digests = rfc_4634_sha256_digests() ++ [long_sha256_digest()], - Keys = rfc_4231_keys() ++ [long_hmac_key(sha256)], - Data = rfc_4231_msgs() ++ [long_msg()], - Hmac = rfc4231_hmac_sha256() ++ [long_hmac(sha256)], - [{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha384 = Type, Config) -> Msgs = [rfc_4634_test1(), rfc_4634_test2(), long_msg()], Digests = rfc_4634_sha384_digests() ++ [long_sha384_digest()], - Keys = rfc_4231_keys() ++ [long_hmac_key(sha384)], - Data = rfc_4231_msgs() ++ [long_msg()], - Hmac = rfc4231_hmac_sha384() ++ [long_hmac(sha384)], - [{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha512 = Type, Config) -> Msgs = [rfc_4634_test1(), rfc_4634_test2(), long_msg()], Digests = rfc_4634_sha512_digests() ++ [long_sha512_digest()], - Keys = rfc_4231_keys() ++ [long_hmac_key(sha512)], - Data = rfc_4231_msgs() ++ [long_msg()], - Hmac = rfc4231_hmac_sha512() ++ [long_hmac(sha512)], - [{hash, {Type, Msgs, Digests}}, {hmac, {Type, Keys, Data, Hmac}} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha3_224 = Type, Config) -> {Msgs,Digests} = sha3_test_vectors(Type), - [{hash, {Type, Msgs, Digests}}, {hmac, hmac_sha3(Type)} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha3_256 = Type, Config) -> {Msgs,Digests} = sha3_test_vectors(Type), - [{hash, {Type, Msgs, Digests}}, {hmac, hmac_sha3(Type)} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha3_384 = Type, Config) -> {Msgs,Digests} = sha3_test_vectors(Type), - [{hash, {Type, Msgs, Digests}}, {hmac, hmac_sha3(Type)} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(sha3_512 = Type, Config) -> {Msgs,Digests} = sha3_test_vectors(Type), - [{hash, {Type, Msgs, Digests}}, {hmac, hmac_sha3(Type)} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(blake2b = Type, Config) -> {Msgs, Digests} = blake2_test_vectors(Type), - [{hash, {Type, Msgs, Digests}}, {hmac, blake2_hmac(Type)} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(blake2s = Type, Config) -> {Msgs, Digests} = blake2_test_vectors(Type), - [{hash, {Type, Msgs, Digests}}, {hmac, blake2_hmac(Type)} | Config]; + [{hash, {Type, Msgs, Digests}} | Config]; group_config(rsa, Config) -> Msg = rsa_plain(), Public = rsa_public(), @@ -1828,7 +1858,6 @@ group_config(Type, Config) when Type == ed25519 ; Type == ed448 -> group_config(srp, Config) -> GenerateCompute = [srp3(), srp6(), srp6a(), srp6a_smaller_prime()], [{generate_compute, GenerateCompute} | Config]; - group_config(ecdh, Config) -> Compute = ecdh(), Generate = ecc(), @@ -1836,19 +1865,6 @@ group_config(ecdh, Config) -> group_config(dh, Config) -> GenerateCompute = [dh()], [{generate_compute, GenerateCompute} | Config]; - -group_config(aes_cbc128 = Type, Config) -> - Block = fun() -> aes_cbc128(Config) end, - Pairs = fun() -> cmac_nist(Config, Type) end, - [{cipher, Block}, {cmac, Pairs} | Config]; -group_config(aes_cbc256 = Type, Config) -> - Block = fun() -> aes_cbc256(Config) end, - Pairs = fun() -> cmac_nist(Config, Type) end, - [{cipher, Block}, {cmac, Pairs} | Config]; -group_config(chacha20_poly1305, Config) -> - AEAD = chacha20_poly1305(Config), - [{cipher, AEAD} | Config]; - group_config(poly1305, Config) -> V = [%% {Key, Txt, Expect} {%% RFC7539 2.5.2 @@ -1864,6 +1880,76 @@ group_config(F, Config) -> [{cipher, TestVectors} | Config]. +configure_mac(MacType, SubType, Config) -> + case do_configure_mac(MacType, SubType, Config) of + undefined -> + {skip, io:format("No ~p test vectors for ~p", [MacType, SubType])}; + Pairs -> + [{MacType, Pairs} | Config] + end. + +do_configure_mac(hmac, Type, _Config) -> + case Type of + md5 -> + Keys = rfc_2202_md5_keys() ++ [long_hmac_key(md5)], + Data = rfc_2202_msgs() ++ [long_msg()], + Hmac = rfc_2202_hmac_md5() ++ [long_hmac(md5)], + zip3_special(hmac, Type, Keys, Data, Hmac); + sha -> + Keys = rfc_2202_sha_keys() ++ [long_hmac_key(sha)], + Data = rfc_2202_msgs() ++ [long_msg()], + Hmac = rfc_2202_hmac_sha() ++ [long_hmac(sha)], + zip3_special(hmac, Type, Keys, Data, Hmac); + sha224 -> + Keys = rfc_4231_keys(), + Data = rfc_4231_msgs(), + Hmac = rfc4231_hmac_sha224(), + zip3_special(hmac, Type, Keys, Data, Hmac); + sha256 -> + Keys = rfc_4231_keys() ++ [long_hmac_key(sha256)], + Data = rfc_4231_msgs() ++ [long_msg()], + Hmac = rfc4231_hmac_sha256() ++ [long_hmac(sha256)], + zip3_special(hmac, Type, Keys, Data, Hmac); + sha384 -> + Keys = rfc_4231_keys() ++ [long_hmac_key(sha384)], + Data = rfc_4231_msgs() ++ [long_msg()], + Hmac = rfc4231_hmac_sha384() ++ [long_hmac(sha384)], + zip3_special(hmac, Type, Keys, Data, Hmac); + sha512 -> + Keys = rfc_4231_keys() ++ [long_hmac_key(sha512)], + Data = rfc_4231_msgs() ++ [long_msg()], + Hmac = rfc4231_hmac_sha512() ++ [long_hmac(sha512)], + zip3_special(hmac, Type, Keys, Data, Hmac); + sha3_224 -> + hmac_sha3(Type); + sha3_256 -> + hmac_sha3(Type); + sha3_384 -> + hmac_sha3(Type); + sha3_512 -> + hmac_sha3(Type); + blake2b -> + blake2_hmac(Type); + blake2s -> + blake2_hmac(Type); + _ -> + undefined + end; +do_configure_mac(cmac, Cipher, Config) -> + case Cipher of + aes_128_cbc -> + fun() -> read_rsp(Config, Cipher, ["CMACGenAES128.rsp", "CMACVerAES128.rsp"]) end; + aes_256_cbc -> + fun() -> read_rsp(Config, Cipher, ["CMACGenAES256.rsp", "CMACVerAES256.rsp"]) end; + _ -> + undefined + end. + + +zip3_special(Type, SubType, As, Bs, Cs) -> + [{Type, SubType, A, B, C} + || {A,B,C} <- lists:zip3(As, Bs, Cs)]. + rsa_sign_verify_tests(Config, Msg, Public, Private, PublicS, PrivateS, OptsToTry) -> case ?config(fips, Config) of @@ -1981,10 +2067,8 @@ blake2_test_vectors(blake2s) -> ]}. blake2_hmac(Type) -> - {Ks, Ds, Hs} = lists:unzip3( - [ {hexstr2bin(K), hexstr2bin(D), H} - || {{K, D}, H} <- lists:zip(blake2_hmac_key_data(), blake2_hmac_hmac(Type)) ]), - {Type, Ks, Ds, Hs}. + [{hmac, Type, hexstr2bin(K), hexstr2bin(D), H} + || {{K, D}, H} <- lists:zip(blake2_hmac_key_data(), blake2_hmac_hmac(Type)) ]. blake2_hmac_key_data() -> [ {"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b 0b0b0b0b", @@ -2083,12 +2167,8 @@ hmac_sha3(Type) -> sha3_384 -> 3; sha3_512 -> 4 end, - {Keys, Datas, Hmacs} = - lists:unzip3( - [{hexstr2bin(Key), hexstr2bin(Data), hexstr2bin(element(N,Hmacs))} - || {Key,Data,Hmacs} <- hmac_sha3_data()]), - {Type, Keys, Datas, Hmacs}. - + [{hmac, Type, hexstr2bin(Key), hexstr2bin(Data), hexstr2bin(element(N,Hmacs))} + || {Key,Data,Hmacs} <- hmac_sha3_data()]. hmac_sha3_data() -> [ @@ -3843,14 +3923,6 @@ ecc() -> end, TestCases). -cmac_nist(Config, aes_cbc128 = Type) -> - read_rsp(Config, Type, - ["CMACGenAES128.rsp", "CMACVerAES128.rsp"]); - -cmac_nist(Config, aes_cbc256 = Type) -> - read_rsp(Config, Type, - ["CMACGenAES256.rsp", "CMACVerAES256.rsp"]). - int_to_bin(X) when X < 0 -> int_to_bin_neg(X, []); int_to_bin(X) -> int_to_bin_pos(X, []). @@ -4068,12 +4140,11 @@ parse_rsp_cmac(Type, Key0, Msg0, Mlen0, Tlen, MAC0, Next, State, Acc) -> Mlen = binary_to_integer(Mlen0), <<Msg:Mlen/bytes, _/binary>> = hexstr2bin(Msg0), MAC = hexstr2bin(MAC0), - case binary_to_integer(Tlen) of 0 -> - parse_rsp(Type, Next, State, [{Type, Key, Msg, MAC}|Acc]); + parse_rsp(Type, Next, State, [{cmac, Type, Key, Msg, MAC}|Acc]); I -> - parse_rsp(Type, Next, State, [{Type, Key, Msg, I, MAC}|Acc]) + parse_rsp(Type, Next, State, [{cmac, Type, Key, Msg, I, MAC}|Acc]) end. api_errors_ecdh(Config) when is_list(Config) -> diff --git a/lib/dialyzer/doc/src/dialyzer.xml b/lib/dialyzer/doc/src/dialyzer.xml index 443de7b0dd..8dd814982d 100644 --- a/lib/dialyzer/doc/src/dialyzer.xml +++ b/lib/dialyzer/doc/src/dialyzer.xml @@ -537,7 +537,10 @@ Option :: {files, [Filename :: string()]} 'plt_check' | 'plt_remove'} | {warnings, [WarnOpts]} - | {get_warnings, bool()} + | {get_warnings, boolean()} + | {native, boolean()} + %% Defaults to false when invoked from Erlang + | {native_cache, boolean()} WarnOpts :: error_handling | no_behaviours diff --git a/lib/dialyzer/src/dialyzer.hrl b/lib/dialyzer/src/dialyzer.hrl index 4a12b9b671..e1821f10eb 100644 --- a/lib/dialyzer/src/dialyzer.hrl +++ b/lib/dialyzer/src/dialyzer.hrl @@ -160,7 +160,9 @@ indent_opt = ?INDENT_OPT :: iopt(), callgraph_file = "" :: file:filename(), check_plt = true :: boolean(), - solvers = [] :: [solver()]}). + solvers = [] :: [solver()], + native = maybe :: boolean() | 'maybe', + native_cache = true :: boolean()}). -record(contract, {contracts = [] :: [contract_pair()], args = [] :: [erl_types:erl_type()], diff --git a/lib/dialyzer/src/dialyzer_cl_parse.erl b/lib/dialyzer/src/dialyzer_cl_parse.erl index 280cae81d5..cadc2116b0 100644 --- a/lib/dialyzer/src/dialyzer_cl_parse.erl +++ b/lib/dialyzer/src/dialyzer_cl_parse.erl @@ -316,7 +316,9 @@ common_options() -> {use_spec, get(dialyzer_options_use_contracts)}, {warnings, get(dialyzer_warnings)}, {check_plt, get(dialyzer_options_check_plt)}, - {solvers, get(dialyzer_solvers)}]. + {solvers, get(dialyzer_solvers)}, + {native, get(dialyzer_options_native)}, + {native_cache, get(dialyzer_options_native_cache)}]. %%----------------------------------------------------------------------- diff --git a/lib/dialyzer/src/dialyzer_options.erl b/lib/dialyzer/src/dialyzer_options.erl index 3b30036c1c..f88f4f8ea2 100644 --- a/lib/dialyzer/src/dialyzer_options.erl +++ b/lib/dialyzer/src/dialyzer_options.erl @@ -197,6 +197,10 @@ build_options([{OptionName, Value} = Term|Rest], Options) -> solvers -> assert_solvers(Value), build_options(Rest, Options#options{solvers = Value}); + native -> + build_options(Rest, Options#options{native = Value}); + native_cache -> + build_options(Rest, Options#options{native_cache = Value}); _ -> bad_option("Unknown dialyzer command line option", Term) end; diff --git a/lib/erl_interface/src/decode/decode_fun.c b/lib/erl_interface/src/decode/decode_fun.c index 3a7a2b01c1..db71007505 100644 --- a/lib/erl_interface/src/decode/decode_fun.c +++ b/lib/erl_interface/src/decode/decode_fun.c @@ -77,10 +77,12 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p) } if (p != NULL) { p->u.closure.n_free_vars = n; - p->u.closure.free_var_len = ix - ix0; - p->u.closure.free_vars = ei_malloc(ix - ix0); - if (!(p->u.closure.free_vars)) return -1; - memcpy(p->u.closure.free_vars, s + ix0, ix - ix0); + p->u.closure.free_var_len = ix - ix0; + if (p->u.closure.free_var_len > 0) { + p->u.closure.free_vars = ei_malloc(p->u.closure.free_var_len); + if (!(p->u.closure.free_vars)) return -1; + memcpy(p->u.closure.free_vars, s + ix0, p->u.closure.free_var_len); + } } s += ix; *index += s-s0; @@ -146,6 +148,7 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p) else { p_arity = NULL; } + ix = 0; if (ei_decode_atom_as(s, &ix, p_module, MAXATOMLEN_UTF8, ERLANG_UTF8, NULL, NULL) < 0) return -1; @@ -171,6 +174,8 @@ int ei_decode_fun(const char *buf, int *index, erlang_fun *p) } if (ei_decode_long(s, &ix, p_arity) < 0) return -1; + s += ix; + *index += s - s0; return 0; } default: diff --git a/lib/erl_interface/src/decode/decode_skip.c b/lib/erl_interface/src/decode/decode_skip.c index 736c00e074..0622ce7d59 100644 --- a/lib/erl_interface/src/decode/decode_skip.c +++ b/lib/erl_interface/src/decode/decode_skip.c @@ -97,6 +97,7 @@ int ei_skip_term(const char* buf, int* index) break; case ERL_FUN_EXT: case ERL_NEW_FUN_EXT: + case ERL_EXPORT_EXT: if (ei_decode_fun(buf, index, NULL) < 0) return -1; break; default: diff --git a/lib/erl_interface/src/misc/ei_printterm.c b/lib/erl_interface/src/misc/ei_printterm.c index 5c40fb7747..aee7f7eeb0 100644 --- a/lib/erl_interface/src/misc/ei_printterm.c +++ b/lib/erl_interface/src/misc/ei_printterm.c @@ -121,8 +121,10 @@ static int print_term(FILE* fp, ei_x_buff* x, erlang_pid pid; erlang_port port; erlang_ref ref; + erlang_fun fun; double d; long l; + const char* delim; int tindex = *index; @@ -239,41 +241,47 @@ static int print_term(FILE* fp, ei_x_buff* x, m = BINPRINTSIZE; else m = l; - --m; + delim = ""; for (i = 0; i < m; ++i) { - ch_written += xprintf(fp, x, "%d,", p[i]); + ch_written += xprintf(fp, x, "%s%u", delim, (unsigned char)p[i]); + delim = ","; } - ch_written += xprintf(fp, x, "%d", p[i]); if (l > BINPRINTSIZE) ch_written += xprintf(fp, x, ",..."); xputc('>', fp, x); ++ch_written; ei_free(p); break; case ERL_BIT_BINARY_EXT: { - const char* cp; + const unsigned char* cp; size_t bits; unsigned int bitoffs; int trunc = 0; - if (ei_decode_bitstring(buf, index, &cp, &bitoffs, &bits) < 0 + if (ei_decode_bitstring(buf, index, (const char**)&cp, &bitoffs, &bits) < 0 || bitoffs != 0) { goto err; } ch_written += xprintf(fp, x, "#Bits<"); - m = (bits+7) / 8; - if (m > BINPRINTSIZE) { + if ((bits+7) / 8 > BINPRINTSIZE) { m = BINPRINTSIZE; trunc = 1; } - --m; + else + m = bits / 8; + + delim = ""; for (i = 0; i < m; ++i) { - ch_written += xprintf(fp, x, "%d,", cp[i]); + ch_written += xprintf(fp, x, "%s%u", delim, cp[i]); + delim = ","; } - ch_written += xprintf(fp, x, "%d", cp[i]); if (trunc) ch_written += xprintf(fp, x, ",..."); - else if (bits % 8 != 0) - ch_written += xprintf(fp, x, ":%u", (unsigned)(bits % 8)); + else { + bits %= 8; + if (bits) + ch_written += xprintf(fp, x, "%s%u:%u", delim, + (cp[i] >> (8-bits)), bits); + } xputc('>', fp, x); ++ch_written; break; } @@ -306,12 +314,46 @@ static int print_term(FILE* fp, ei_x_buff* x, } break; - case ERL_FLOAT_EXT: case NEW_FLOAT_EXT: if (ei_decode_double(buf, index, &d) < 0) goto err; ch_written += xprintf(fp, x, "%f", d); break; + case ERL_MAP_EXT: + if (ei_decode_map_header(buf, &tindex, &n) < 0) goto err; + ch_written += xprintf(fp, x, "#{"); + for (i = 0; i < n; ++i) { + r = print_term(fp, x, buf, &tindex); + if (r < 0) goto err; + ch_written += r; + ch_written += xprintf(fp, x, " => "); + r = print_term(fp, x, buf, &tindex); + if (r < 0) goto err; + ch_written += r; + if (i < n-1) { + xputs(", ", fp, x); ch_written += 2; + } + } + *index = tindex; + xputc('}', fp, x); ch_written++; + break; + case ERL_FUN_EXT: + case ERL_NEW_FUN_EXT: + case ERL_EXPORT_EXT: + if (ei_decode_fun(buf, &tindex, &fun) < 0) goto err; + if (fun.type == EI_FUN_EXPORT) { + ch_written += xprintf(fp, x, "fun %s:%s/%ld", + fun.module, + fun.u.exprt.func, + fun.arity); + } else { + ch_written += xprintf(fp, x, "#Fun{%s.%ld.%lu}", + fun.module, + fun.u.closure.index, + fun.u.closure.uniq); + } + *index = tindex; + break; default: goto err; } diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE.erl b/lib/erl_interface/test/ei_decode_encode_SUITE.erl index 3451d9f503..0204b4cfd6 100644 --- a/lib/erl_interface/test/ei_decode_encode_SUITE.erl +++ b/lib/erl_interface/test/ei_decode_encode_SUITE.erl @@ -48,7 +48,8 @@ init_per_testcase(Case, Config) -> test_ei_decode_encode(Config) when is_list(Config) -> P = runner:start(Config, ?test_ei_decode_encode), - Fun = fun (X) -> {X,true} end, + Fun1 = fun (X) -> {X,true} end, + Fun2 = fun runner:init_per_testcase/3, Pid = self(), Port = case os:type() of {win32,_} -> @@ -70,7 +71,8 @@ test_ei_decode_encode(Config) when is_list(Config) -> BigLargeB = 1 bsl 11112 + BigSmallB, BigLargeC = BigSmallA * BigSmallB * BigSmallC * BigSmallA, - send_rec(P, Fun), + send_rec(P, Fun1), + send_rec(P, Fun2), send_rec(P, Pid), send_rec(P, Port), send_rec(P, Ref), @@ -115,7 +117,7 @@ test_ei_decode_encode(Config) when is_list(Config) -> send_rec(P, {}), send_rec(P, {atom, Pid, Port, Ref}), send_rec(P, [atom, Pid, Port, Ref]), - send_rec(P, [atom | Fun]), + send_rec(P, [atom | Fun1]), send_rec(P, #{}), send_rec(P, #{key => value}), send_rec(P, maps:put(Port, Ref, #{key => value, key2 => Pid})), diff --git a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c index 85ca6c56e9..512f9ed0c7 100644 --- a/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c +++ b/lib/erl_interface/test/ei_decode_encode_SUITE_data/ei_decode_encode_test.c @@ -564,6 +564,7 @@ TESTCASE(test_ei_decode_encode) ei_init(); decode_encode_one(&fun_type); + decode_encode_one(&fun_type); decode_encode_one(&pid_type); decode_encode_one(&port_type); decode_encode_one(&ref_type); diff --git a/lib/erl_interface/test/ei_print_SUITE.erl b/lib/erl_interface/test/ei_print_SUITE.erl index c75ce55a7d..8a35b22ae5 100644 --- a/lib/erl_interface/test/ei_print_SUITE.erl +++ b/lib/erl_interface/test/ei_print_SUITE.erl @@ -26,7 +26,8 @@ -export([all/0, suite/0, init_per_testcase/2, - atoms/1, tuples/1, lists/1, strings/1]). + atoms/1, tuples/1, lists/1, strings/1, + maps/1, funs/1, binaries/1, bitstrings/1]). -import(runner, [get_term/1]). @@ -36,8 +37,8 @@ suite() -> [{ct_hooks,[ts_install_cth]}]. -all() -> - [atoms, tuples, lists, strings]. +all() -> + [atoms, tuples, lists, strings, maps, funs, binaries, bitstrings]. init_per_testcase(Case, Config) -> runner:init_per_testcase(?MODULE, Case, Config). @@ -142,3 +143,64 @@ strings(Config) when is_list(Config) -> runner:recv_eot(P), ok. + +maps(Config) -> + P = runner:start(Config, ?maps), + + {term, "#{}"} = get_term(P), + {term, "#{key => value}"} = get_term(P), + {term, "#{key => value, another_key => {ok, 42}}"} = get_term(P), + + runner:recv_eot(P), + ok. + +funs(Config) -> + P = runner:start(Config, ?funs), + + {term, "#Fun{some_module.42.3735928559}"} = get_term(P), + {term, "#Fun{some_module.37.195935983}"} = get_term(P), + {term, "fun erlang:abs/1"} = get_term(P), + + runner:recv_eot(P), + ok. + +binaries(Config) -> + P = runner:start(Config, ?binaries), + + "#Bin<>" = send_term_get_printed(P, <<>>), + "#Bin<1,2,3>" = send_term_get_printed(P, <<1,2,3>>), + "#Bin<0,127,128,255>" = send_term_get_printed(P, <<0,127,128,255>>), + Bin30 = <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30>>, + "#Bin<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30>" + = send_term_get_printed(P, Bin30), + "#Bin<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,...>" + = send_term_get_printed(P, <<Bin30/binary,31>>), + + runner:recv_eot(P), + ok. + +bitstrings(Config) -> + P = runner:start(Config, ?bitstrings), + + "#Bits<1:1>" = send_term_get_printed(P, <<1:1>>), + "#Bits<123:7>" = send_term_get_printed(P, <<123:7>>), + "#Bits<1,2,3:4>" = send_term_get_printed(P, <<1,2,3:4>>), + "#Bits<0,127,128,255,126:7>" = send_term_get_printed(P, <<0,127,128,255,-2:7>>), + Bits30 = <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19, + 20,21,22,23,24,25,26,27,28,29,30:5>>, + "#Bits<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30:5>" + = send_term_get_printed(P, Bits30), + "#Bin<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,241>" + = send_term_get_printed(P, <<Bits30/bits,1:3>>), + "#Bits<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,240,...>" + = send_term_get_printed(P, <<Bits30/bits,1:4>>), + + runner:recv_eot(P), + ok. + + + +send_term_get_printed(Port, Term) -> + Port ! {self(), {command, term_to_binary(Term)}}, + {term, String} = get_term(Port), + String. diff --git a/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c index 80be3016e6..27d4153250 100644 --- a/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c +++ b/lib/erl_interface/test/ei_print_SUITE_data/ei_print_test.c @@ -29,6 +29,46 @@ */ static void +send_printed_buf(ei_x_buff* x) +{ + char* b = NULL; + char fn[256]; + char *tmp = getenv("temp"); + FILE* f; + int n, index = 0, ver; + +#ifdef VXWORKS + tmp = "."; +#else + if (tmp == NULL) { + tmp = "/tmp"; + } +#endif + strcpy(fn, tmp); + strcat(fn, "/ei_print_test.txt"); + f = fopen(fn, "w+"); + ei_decode_version(x->buff, &index, &ver); + n = ei_print_term(f, x->buff, &index); + if (n < 0) { + fclose(f); + x->index = 0; + ei_x_format(x, "~s", "ERROR: term decoding failed"); + send_bin_term(x); + } else { + fseek(f, 0, SEEK_SET); + b = malloc(n+1); + fread(b, 1, n, f); + b[n] = '\0'; + fclose(f); + x->index = 0; + ei_x_format(x, "~s", b); + send_bin_term(x); + free(b); + } +} + + +static void send_printed3(char* format, char* p1, char* p2, int fl) { char* b = NULL; @@ -43,25 +83,7 @@ send_printed3(char* format, char* p1, char* p2, int fl) } else { ei_x_format(&x, format, p1, p2); } -#ifdef VXWORKS - tmp = "."; -#else - if (tmp == NULL) tmp = "/tmp"; -#endif - strcpy(fn, tmp); - strcat(fn, "/ei_print_test.txt"); - f = fopen(fn, "w+"); - ei_decode_version(x.buff, &index, &ver); - n = ei_print_term(f, x.buff, &index); - fseek(f, 0, SEEK_SET); - b = malloc(n+1); - fread(b, 1, n, f); - b[n] = '\0'; - fclose(f); - x.index = 0; - ei_x_format(&x, "~s", b); - send_bin_term(&x); - free(b); + send_printed_buf(&x); ei_x_free(&x); } @@ -184,4 +206,146 @@ TESTCASE(strings) report(1); } +TESTCASE(maps) +{ + ei_x_buff x; + + ei_init(); + + ei_x_new_with_version(&x); + ei_x_encode_map_header(&x, 0); + send_printed_buf(&x); + ei_x_free(&x); + + ei_x_new_with_version(&x); + ei_x_encode_map_header(&x, 1); + ei_x_encode_atom(&x, "key"); + ei_x_encode_atom(&x, "value"); + send_printed_buf(&x); + ei_x_free(&x); + + ei_x_new_with_version(&x); + ei_x_encode_map_header(&x, 2); + ei_x_encode_atom(&x, "key"); + ei_x_encode_atom(&x, "value"); + ei_x_encode_atom(&x, "another_key"); + ei_x_encode_tuple_header(&x, 2); + ei_x_encode_atom(&x, "ok"); + ei_x_encode_long(&x, 42L); + send_printed_buf(&x); + ei_x_free(&x); + + report(1); +} + +TESTCASE(funs) +{ + ei_x_buff x; + erlang_pid self; + erlang_fun fun; + + strcpy(self.node, "node@host"); + self.num = 9; + self.serial = 99; + self.creation = 1; + + ei_init(); + + ei_x_new_with_version(&x); + fun.arity = -1; /* Will encode as FUN_EXT */ + strcpy(fun.module, "some_module"); + fun.type = EI_FUN_CLOSURE; + fun.u.closure.pid = self; + fun.u.closure.index = fun.u.closure.old_index = 42; + fun.u.closure.uniq = 0xDEADBEEF; + fun.u.closure.n_free_vars = 0; + fun.u.closure.free_var_len = 0; + ei_x_encode_fun(&x, &fun); + send_printed_buf(&x); + ei_x_free(&x); + + ei_x_new_with_version(&x); + fun.arity = 0; /* Will encode as NEW_FUN_EXT */ + strcpy(fun.module, "some_module"); + fun.type = EI_FUN_CLOSURE; + fun.u.closure.pid = self; + fun.u.closure.index = fun.u.closure.old_index = 37; + fun.u.closure.uniq = 0xBADBEEF; + fun.u.closure.n_free_vars = 0; + fun.u.closure.free_var_len = 0; + ei_x_encode_fun(&x, &fun); + send_printed_buf(&x); + ei_x_free(&x); + + ei_x_new_with_version(&x); + fun.arity = 1; + strcpy(fun.module, "erlang"); + fun.type = EI_FUN_EXPORT; + fun.u.exprt.func = "abs"; + ei_x_encode_fun(&x, &fun); + send_printed_buf(&x); + ei_x_free(&x); + + report(1); +} + +TESTCASE(binaries) +{ + char *buf; + long len; + int err, n, index; + ei_x_buff x; + + ei_init(); + + for (n = 5; n; n--) { + buf = read_packet(NULL); + + index = 0; + err = ei_decode_version(buf, &index, NULL); + if (err != 0) + fail1("ei_decode_version returned %d", err); + err = ei_decode_binary(buf, &index, NULL, &len); + if (err != 0) + fail1("ei_decode_binary returned %d", err); + + ei_x_new(&x); + ei_x_append_buf(&x, buf, index); + send_printed_buf(&x); + ei_x_free(&x); + + free_packet(buf); + } + report(1); +} + +TESTCASE(bitstrings) +{ + char *buf; + long len; + int err, n, index; + ei_x_buff x; + + ei_init(); + + for (n = 7; n; n--) { + buf = read_packet(NULL); + + index = 0; + err = ei_decode_version(buf, &index, NULL); + if (err != 0) + fail1("ei_decode_version returned %d", err); + err = ei_decode_bitstring(buf, &index, NULL, NULL, NULL); + if (err != 0) + fail1("ei_decode_bitstring returned %d", err); + + ei_x_new(&x); + ei_x_append_buf(&x, buf, index); + send_printed_buf(&x); + ei_x_free(&x); + + free_packet(buf); + } + report(1); +} diff --git a/lib/inets/src/http_server/httpd_request.erl b/lib/inets/src/http_server/httpd_request.erl index 9d7538a13d..f3e24263b8 100644 --- a/lib/inets/src/http_server/httpd_request.erl +++ b/lib/inets/src/http_server/httpd_request.erl @@ -196,9 +196,9 @@ parse_headers(<<?CR,?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max, Options, Result); -parse_headers(<<?LF,?LF,Body/binary>>, [], [], Current, Max, Options, Result) -> +parse_headers(<<?LF,?LF,Body/binary>>, Header, Headers, Current, Max, Options, Result) -> %% If ?CR is is missing RFC2616 section-19.3 - parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], Current, Max, + parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, Header, Headers, Current, Max, Options, Result); parse_headers(<<?CR,?LF,?CR,?LF,Body/binary>>, [], [], _, _, _, Result) -> diff --git a/lib/inets/test/http_format_SUITE.erl b/lib/inets/test/http_format_SUITE.erl index 0a5aed67d5..3ff3ed4e97 100644 --- a/lib/inets/test/http_format_SUITE.erl +++ b/lib/inets/test/http_format_SUITE.erl @@ -414,6 +414,19 @@ http_request(Config) when is_list(Config) -> {max_content_length, ?HTTP_MAX_CONTENT_LENGTH} ]], HttpHead2), + %% If ?CR is is missing RFC2616 section-19.3 + HttpHead3 = ["GET http://www.erlang.org HTTP/1.1", [?LF], + "Accept: text/html", [?LF, ?LF]], + {"GET", + "http://www.erlang.org", + "HTTP/1.1", + {#http_request_h{}, [{"accept","text/html"}]}, <<>>} = + parse(httpd_request, parse, [[{max_header, ?HTTP_MAX_HEADER_SIZE}, + {max_version, ?HTTP_MAX_VERSION_STRING}, + {max_method, ?HTTP_MAX_METHOD_STRING}, + {max_content_length, ?HTTP_MAX_CONTENT_LENGTH} + ]], HttpHead3), + %% Note the following body is not related to the headers above HttpBody = ["<HTML>\n<HEAD>\n<TITLE> dummy </TITLE>\n</HEAD>\n<BODY>\n", "<H1>dummy</H1>\n</BODY>\n</HTML>\n"], diff --git a/lib/inets/test/httpc_SUITE.erl b/lib/inets/test/httpc_SUITE.erl index 1d37e71847..8ca4f21928 100644 --- a/lib/inets/test/httpc_SUITE.erl +++ b/lib/inets/test/httpc_SUITE.erl @@ -2191,7 +2191,7 @@ check_cookie([_Head | Tail]) -> content_length([]) -> 0; -content_length(["content-length:" ++ Value | _]) -> +content_length([{"content-length", Value}|_]) -> list_to_integer(string:strip(Value)); content_length([_Head | Tail]) -> content_length(Tail). diff --git a/lib/kernel/doc/src/Makefile b/lib/kernel/doc/src/Makefile index f8867ccf25..70623ab9aa 100644 --- a/lib/kernel/doc/src/Makefile +++ b/lib/kernel/doc/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2018. All Rights Reserved. +# Copyright Ericsson AB 1997-2019. 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. @@ -36,6 +36,18 @@ RELSYSDIR = $(RELEASE_PATH)/lib/$(APPLICATION)-$(VSN) # Target Specs # ---------------------------------------------------- XML_APPLICATION_FILES = ref_man.xml + +# The doc build has problems with if-defing out modules... +ifeq ($(USE_ESOCK),yes) +XML_REF3_ESOCK_FILES = net.xml +ESOCK_USE_NET_XML=<xi:include href="net.xml"\/> +ESOCK_USE_NET_SPECS_XML=<xi:include href="../specs/specs_net.xml"/> +else +XML_REF3_ESOCK_FILES = +ESOCK_USE_NET_SPECS_XML = +ESOCK_USE_NET_XML = +endif + XML_REF3_FILES = application.xml \ auth.xml \ code.xml \ @@ -62,6 +74,7 @@ XML_REF3_FILES = application.xml \ logger_disk_log_h.xml \ logger_filters.xml \ logger_formatter.xml \ + $(XML_REF3_ESOCK_FILES) \ net_adm.xml \ net_kernel.xml \ os.xml \ @@ -112,6 +125,7 @@ SPECS_FILES = $(XML_REF3_FILES:%.xml=$(SPECDIR)/specs_%.xml) TOP_SPECS_FILE = specs.xml + # ---------------------------------------------------- # FIGURES # ---------------------------------------------------- @@ -138,7 +152,7 @@ SPECS_FLAGS = -I../../include $(HTMLDIR)/%: % $(INSTALL_DATA) $< $@ -docs: man pdf html +docs: ref_man specs man pdf html $(TOP_PDF_FILE): $(XML_FILES) @@ -148,19 +162,32 @@ html: images $(HTML_REF_MAN_FILE) man: $(MAN3_FILES) $(MAN4_FILES) $(MAN6_FILES) +ref_man: ref_man.xml +specs: specs.xml + images: $(IMAGE_FILES:%=$(HTMLDIR)/%) +info: + @echo "XML_APPLICATION_FILES: $(XML_APPLICATION_FILES)" + @echo "XML_REF3_ESOCK_FILES: $(XML_REF3_ESOCK_FILES)" + @echo "XML_REF3_FILES: $(XML_REF3_FILES)" + @echo "XML_REF4_FILES: $(XML_REF4_FILES)" + @echo "XML_REF6_FILES: $(XML_REF6_FILES)" + @echo "XML_PART_FILES: $(XML_PART_FILES)" + @echo "XML_CHAPTER_FILES: $(XML_CHAPTER_FILES)" + @echo "BOOK_FILES: $(BOOK_FILES)" + debug opt: clean clean_docs: rm -rf $(HTMLDIR)/* rm -rf $(XMLDIR) - rm -f $(MAN3DIR)/* - rm -f $(MAN4DIR)/* - rm -f $(MAN6DIR)/* - rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) - rm -f $(SPECDIR)/* - rm -f errs core *~ *.eps + rm -f $(MAN3DIR)/* + rm -f $(MAN4DIR)/* + rm -f $(MAN6DIR)/* + rm -f $(TOP_PDF_FILE) $(TOP_PDF_FILE:%.pdf=%.fo) + rm -f $(SPECDIR)/* + rm -f errs core *~ *.eps $(SPECDIR)/specs_erl_prim_loader_stub.xml: $(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ @@ -175,6 +202,14 @@ $(SPECDIR)/specs_zlib_stub.xml: $(gen_verbose)escript $(SPECS_EXTRACTOR) $(SPECS_FLAGS) \ -o$(dir $@) -module zlib_stub +ref_man.xml: ref_man.xml.src + ($(PERL) -p -e 's?%ESOCK_USE_NET_XML%?$(ESOCK_USE_NET_XML)?' \ + $<) > $@ +specs.xml: specs.xml.src + ($(PERL) -p -e 's?%ESOCK_USE_NET_SPECS_XML%?$(ESOCK_USE_NET_SPECS_XML)?' \ + $<) > $@ + + # ---------------------------------------------------- # Release Target # ---------------------------------------------------- diff --git a/lib/kernel/doc/src/gen_udp.xml b/lib/kernel/doc/src/gen_udp.xml index d20fc1fdfd..6c0d072fed 100644 --- a/lib/kernel/doc/src/gen_udp.xml +++ b/lib/kernel/doc/src/gen_udp.xml @@ -213,12 +213,93 @@ </func> <func> - <name name="send" arity="4" since=""/> + <name name="send" arity="3" since="OTP @OTP-15747@"/> <fsummary>Send a packet.</fsummary> <desc> <p> - Sends a packet to the specified address and port. Argument - <c><anno>Address</anno></c> can be a hostname or a socket address. + Sends a packet to the specified <c><anno>Destination</anno></c>. + </p> + <p> + This function is equivalent to + <seealso marker="#send-4-AncData"><c>send(<anno>Socket</anno>, <anno>Destination</anno>, [], <anno>Packet</anno>)</c></seealso>. + </p> + </desc> + </func> + + <func> + <name name="send" arity="4" clause_i="1" since=""/> + <fsummary>Send a packet.</fsummary> + <desc> + <p> + Sends a packet to the specified <c><anno>Host</anno></c> + and <c><anno>Port</anno></c>. + </p> + <p> + This clause is equivalent to + <seealso marker="#send/5"><c>send(<anno>Socket</anno>, <anno>Host</anno>, <anno>Port</anno>, [], <anno>Packet</anno>)</c></seealso>. + </p> + </desc> + </func> + + <func> + <name name="send" arity="4" clause_i="2" anchor="send-4-AncData" since="OTP @OTP-15747@"/> + <fsummary>Send a packet.</fsummary> + <desc> + <p> + Sends a packet to the specified <c><anno>Destination</anno></c> + with ancillary data <c><anno>AncData</anno></c>. + </p> + <note> + <p> + The ancillary data <c><anno>AncData</anno></c> + contains options that for this single message + override the default options for the socket, + an operation that may not be supported on all platforms, + and if so return <c>{error, einval}</c>. + Using more than one of an ancillary data item type + may also not be supported. + <c><anno>AncData</anno> =:= []</c> is always supported. + </p> + </note> + </desc> + </func> + + <func> + <name name="send" arity="4" clause_i="3" since="OTP @OTP-15747@"/> + <fsummary>Send a packet.</fsummary> + <desc> + <p> + Sends a packet to the specified <c><anno>Destination</anno></c>. + Since <c><anno>Destination</anno></c> is complete, + <c><anno>PortZero</anno></c> is redundant and has to be <c>0</c>. + </p> + <p> + This is a legacy clause mostly for + <c><anno>Destination</anno> = {local, Binary}</c> + where <c><anno>PortZero</anno></c> is superfluous. + It is equivalent to + <seealso marker="#send-4-AncData"><c>send(<anno>Socket</anno>, <anno>Destination</anno>, [], <anno>Packet</anno>)</c></seealso>, the clause right above here. + </p> + </desc> + </func> + + <func> + <name name="send" arity="5" since="OTP @OTP-15747@"/> + <fsummary>Send a packet.</fsummary> + <desc> + <p> + Sends a packet to the specified <c><anno>Host</anno></c> + and <c><anno>Port</anno></c>, + with ancillary data <c><anno>AncData</anno></c>. + </p> + <p> + Argument <c><anno>Host</anno></c> can be + a hostname or a socket address, + and <c><anno>Port</anno></c> can be a port number + or a service name atom. + These are resolved into a <c>Destination</c> and after that + this function is equivalent to + <seealso marker="#send-4-AncData"><c>send(<anno>Socket</anno>, Destination, <anno>AncData</anno>, <anno>Packet</anno>)</c></seealso>, read there about ancillary data. </p> </desc> </func> diff --git a/lib/kernel/doc/src/inet.xml b/lib/kernel/doc/src/inet.xml index d4678ca5db..1011befca0 100644 --- a/lib/kernel/doc/src/inet.xml +++ b/lib/kernel/doc/src/inet.xml @@ -118,6 +118,42 @@ fe80::204:acff:fe17:bf38 <name name="port_number"/> </datatype> <datatype> + <name name="family_address" since="OTP @OTP-15747@"/> + <desc> + <p> + A general address format on the form <c>{Family, Destination}</c> + where <c>Family</c> is an atom such as <c>local</c> + and the format of <c>Destination</c> depends on <c>Family</c>, + and is a complete address + (for example an IP address including port number). + </p> + </desc> + </datatype> + <datatype> + <name name="inet_address" since="OTP @OTP-15747@"/> + <desc> + <warning> + <p> + This address format is for now experimental + and for completeness to make all address families have a + <c>{Family, Destination}</c> representation. + </p> + </warning> + </desc> + </datatype> + <datatype> + <name name="inet6_address" since="OTP @OTP-15747@"/> + <desc> + <warning> + <p> + This address format is for now experimental + and for completeness to make all address families have a + <c>{Family, Destination}</c> representation. + </p> + </warning> + </desc> + </datatype> + <datatype> <name name="local_address"/> <desc> <p> @@ -180,12 +216,16 @@ fe80::204:acff:fe17:bf38 <name name="ancillary_data"/> <desc> <p> - Ancillary data received with the data packet - or read with the socket option + Ancillary data received with the data packet, + read with the socket option <seealso marker="gen_tcp#type-pktoptions_value"> <c>pktoptions</c> </seealso> - from a TCP socket. + from a TCP socket, + or to set in a call to + <seealso marker="gen_udp#send-4-AncData"><c>gen_udp:send/4</c></seealso> + or + <seealso marker="gen_udp#send/5"><c>gen_udp:send/5</c></seealso>. </p> <p> The value(s) correspond to the currently active socket @@ -193,7 +233,9 @@ fe80::204:acff:fe17:bf38 <seealso marker="inet#option-recvtos"><c>recvtos</c></seealso>, <seealso marker="inet#option-recvtclass"><c>recvtclass</c></seealso> and - <seealso marker="inet#option-recvttl"><c>recvttl</c></seealso>. + <seealso marker="inet#option-recvttl"><c>recvttl</c></seealso>, + or for a single send operation the option(s) to override + the currently active socket option(s). </p> </desc> </datatype> diff --git a/lib/kernel/doc/src/logger_chapter.xml b/lib/kernel/doc/src/logger_chapter.xml index 1aa4b7a3a2..5aa2caadf0 100644 --- a/lib/kernel/doc/src/logger_chapter.xml +++ b/lib/kernel/doc/src/logger_chapter.xml @@ -89,8 +89,8 @@ <p>Filter functions can be used for more sophisticated filtering than the log level check provides. A filter function can stop or pass a log event, based on any of the event's contents. It can - also modify all parts of the log event. See see - section <seealso marker="#filters">Filters</seealso> for more + also modify all parts of the log event. See section + <seealso marker="#filters">Filters</seealso> for more details.</p> <p>If a log event passes through all primary filters and all handler filters for a specific handler, Logger forwards the diff --git a/lib/kernel/doc/src/net.xml b/lib/kernel/doc/src/net.xml new file mode 100644 index 0000000000..6fbc37076c --- /dev/null +++ b/lib/kernel/doc/src/net.xml @@ -0,0 +1,129 @@ +<?xml version="1.0" encoding="utf-8" ?> +<!DOCTYPE erlref SYSTEM "erlref.dtd"> + +<erlref> + <header> + <copyright> + <year>2018</year><year>2018</year> + <holder>Ericsson AB. All Rights Reserved.</holder> + </copyright> + <legalnotice> + 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. + + </legalnotice> + + <title>net</title> + <prepared></prepared> + <docno></docno> + <date></date> + <rev></rev> + <file>net.xml</file> + </header> + <module since="OTP 22.0">net</module> + <modulesummary>Network interface.</modulesummary> + <description> + <p>This module provides an API for the network interface.</p> + <note> + <p>There is currently <em>no</em> support for Windows. </p> + </note> + </description> + + <datatypes> + <datatype> + <name name="address_info"/> + </datatype> + <datatype> + <name name="name_info"/> + </datatype> + <datatype> + <name name="name_info_flags"/> + </datatype> + <datatype> + <name name="name_info_flag"/> + </datatype> + <datatype> + <name name="name_info_flag_ext"/> + </datatype> + <datatype> + <name name="network_interface_name"/> + </datatype> + <datatype> + <name name="network_interface_index"/> + </datatype> + </datatypes> + + <funcs> + <func> + <name name="gethostname" arity="0"/> + <fsummary>Get hostname.</fsummary> + <desc> + <p>Returns the name of the current host.</p> + </desc> + </func> + + <func> + <name name="getnameinfo" arity="1" since="OTP 22.0"/> + <name name="getnameinfo" arity="2" since="OTP 22.0"/> + <fsummary>Address-to-name transaltion.</fsummary> + <desc> + <p>Address-to-name translation in a protocol-independant manner.</p> + <p>This function is the inverse of + <seealso marker="#getaddrinfo/1"><c>getaddrinfo</c></seealso>. + It converts a socket address to a corresponding host and service.</p> + </desc> + </func> + + <func> + <name name="getaddrinfo" arity="1" since="OTP 22.0"/> + <name name="getaddrinfo" arity="2" clause_i="1" since="OTP 22.0"/> + <name name="getaddrinfo" arity="2" clause_i="2" since="OTP 22.0"/> + <name name="getaddrinfo" arity="2" clause_i="3" since="OTP 22.0"/> + <fsummary>Network address and service transation.</fsummary> + <desc> + <p>Network address and service translation.</p> + <p>This function is the inverse of + <seealso marker="#getnameinfo/1"><c>getnameinfo</c></seealso>. + It converts host and service to a corresponding socket address.</p> + <p>One of the <c>Host</c> and <c>Service</c> may be <c>undefined</c> + but <em>not</em> both.</p> + </desc> + </func> + + <func> + <name name="if_name2index" arity="1" since="OTP 22.0"/> + <fsummary>Mappings between network interface names and indexes.</fsummary> + <desc> + <p>Mappings between network interface names and indexes.</p> + </desc> + </func> + + <func> + <name name="if_index2name" arity="1" since="OTP 22.0"/> + <fsummary>Mappings between network interface index and names.</fsummary> + <desc> + <p>Mappings between network interface index and names.</p> + </desc> + </func> + + <func> + <name name="if_names" arity="0" since="OTP 22.0"/> + <fsummary>Get network interface names and indexes.</fsummary> + <desc> + <p>Get network interface names and indexes.</p> + </desc> + </func> + + </funcs> + +</erlref> + diff --git a/lib/kernel/doc/src/notes.xml b/lib/kernel/doc/src/notes.xml index b065cc2cd8..4d31eeea3d 100644 --- a/lib/kernel/doc/src/notes.xml +++ b/lib/kernel/doc/src/notes.xml @@ -147,6 +147,37 @@ </section> +<section><title>Kernel 6.3.1.2</title> + + <section><title>Improvements and New Features</title> + <list> + <item> + <p> + The possibility to send ancillary data, in particular the + TOS field, has been added to <c>gen_udp:send/4,5</c>.</p> + <p> + Own Id: OTP-15747 Aux Id: ERIERL-294 </p> + </item> + </list> + </section> + +</section> + +<section><title>Kernel 6.3.1.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fix type spec for <c>seq_trace:set_token/2</c>.</p> + <p> + Own Id: OTP-15858 Aux Id: ERL-700 </p> + </item> + </list> + </section> + +</section> + <section><title>Kernel 6.3.1</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -6045,4 +6076,3 @@ </section> </section> </chapter> - diff --git a/lib/kernel/doc/src/ref_man.xml b/lib/kernel/doc/src/ref_man.xml.src index d3b947527f..72e3409123 100644 --- a/lib/kernel/doc/src/ref_man.xml +++ b/lib/kernel/doc/src/ref_man.xml.src @@ -4,7 +4,7 @@ <application xmlns:xi="http://www.w3.org/2001/XInclude"> <header> <copyright> - <year>1996</year><year>2018</year> + <year>1996</year><year>2019</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -60,6 +60,7 @@ <xi:include href="logger_formatter.xml"/> <xi:include href="logger_std_h.xml"/> <xi:include href="logger_disk_log_h.xml"/> + %ESOCK_USE_NET_XML% <xi:include href="net_adm.xml"/> <xi:include href="net_kernel.xml"/> <xi:include href="os.xml"/> diff --git a/lib/kernel/doc/src/specs.xml b/lib/kernel/doc/src/specs.xml.src index b8c25ca53b..ccb26b9458 100644 --- a/lib/kernel/doc/src/specs.xml +++ b/lib/kernel/doc/src/specs.xml.src @@ -26,6 +26,7 @@ <xi:include href="../specs/specs_logger_formatter.xml"/> <xi:include href="../specs/specs_logger_std_h.xml"/> <xi:include href="../specs/specs_logger_disk_log_h.xml"/> + %ESOCK_USE_NET_SPECS_XML% <xi:include href="../specs/specs_net_adm.xml"/> <xi:include href="../specs/specs_net_kernel.xml"/> <xi:include href="../specs/specs_os.xml"/> diff --git a/lib/kernel/src/Makefile b/lib/kernel/src/Makefile index fcb599859b..88752431eb 100644 --- a/lib/kernel/src/Makefile +++ b/lib/kernel/src/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2018. All Rights Reserved. +# Copyright Ericsson AB 1996-2019. 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. @@ -123,6 +123,7 @@ MODULES = \ logger_server \ logger_simple_h \ logger_sup \ + net \ net_adm \ net_kernel \ os \ @@ -180,6 +181,7 @@ ERL_COMPILE_FLAGS += -Werror endif ERL_COMPILE_FLAGS += -I../include + # ---------------------------------------------------- # Targets # ---------------------------------------------------- diff --git a/lib/kernel/src/code.erl b/lib/kernel/src/code.erl index 7faef93609..964ede9bc9 100644 --- a/lib/kernel/src/code.erl +++ b/lib/kernel/src/code.erl @@ -19,6 +19,8 @@ %% -module(code). +-include_lib("kernel/include/logger.hrl"). + %% This is the interface module to the code server. It also contains %% some implementation details. See also related modules: code_*.erl %% in this directory. @@ -707,8 +709,20 @@ do_s(Lib) -> start_get_mode() -> case init:get_argument(mode) of - {ok,[["embedded"]]} -> - embedded; + {ok, [FirstMode | Rest]} -> + case Rest of + [] -> + ok; + _ -> + ?LOG_WARNING("Multiple -mode given to erl, using the first, ~p", + [FirstMode]) + end, + case FirstMode of + ["embedded"] -> + embedded; + _ -> + interactive + end; _ -> interactive end. diff --git a/lib/kernel/src/gen_sctp.erl b/lib/kernel/src/gen_sctp.erl index d893d44079..a63df54ff9 100644 --- a/lib/kernel/src/gen_sctp.erl +++ b/lib/kernel/src/gen_sctp.erl @@ -217,24 +217,29 @@ peeloff(S, AssocId) when is_port(S), is_integer(AssocId) -> Error -> Error end. --spec connect(Socket, Addr, Port, Opts) -> {ok, Assoc} | {error, inet:posix()} when +-spec connect(Socket, Addr, Port, Opts) -> + {ok, #sctp_assoc_change{state :: 'comm_up'}} | + {error, #sctp_assoc_change{state :: 'cant_assoc'}} | + {error, inet:posix()} + when Socket :: sctp_socket(), Addr :: inet:ip_address() | inet:hostname(), Port :: inet:port_number(), - Opts :: [Opt :: option()], - Assoc :: #sctp_assoc_change{}. + Opts :: [Opt :: option()]. connect(S, Addr, Port, Opts) -> connect(S, Addr, Port, Opts, infinity). -spec connect(Socket, Addr, Port, Opts, Timeout) -> - {ok, Assoc} | {error, inet:posix()} when + {ok, #sctp_assoc_change{state :: 'comm_up'}} | + {error, #sctp_assoc_change{state :: 'cant_assoc'}} | + {error, inet:posix()} + when Socket :: sctp_socket(), Addr :: inet:ip_address() | inet:hostname(), Port :: inet:port_number(), Opts :: [Opt :: option()], - Timeout :: timeout(), - Assoc :: #sctp_assoc_change{}. + Timeout :: timeout(). connect(S, Addr, Port, Opts, Timeout) -> case do_connect(S, Addr, Port, Opts, Timeout, true) of diff --git a/lib/kernel/src/gen_udp.erl b/lib/kernel/src/gen_udp.erl index fad7b2f887..3001948209 100644 --- a/lib/kernel/src/gen_udp.erl +++ b/lib/kernel/src/gen_udp.erl @@ -20,7 +20,7 @@ -module(gen_udp). -export([open/1, open/2, close/1]). --export([send/2, send/4, recv/2, recv/3, connect/3]). +-export([send/2, send/3, send/4, send/5, recv/2, recv/3, connect/3]). -export([controlling_process/2]). -export([fdopen/2]). @@ -125,20 +125,80 @@ open(Port, Opts0) -> close(S) -> inet:udp_close(S). --spec send(Socket, Address, Port, Packet) -> ok | {error, Reason} when +-spec send(Socket, Destination, Packet) -> ok | {error, Reason} when Socket :: socket(), - Address :: inet:socket_address() | inet:hostname(), - Port :: inet:port_number(), + Destination :: {inet:ip_address(), inet:port_number()} | + inet:family_address(), + Packet :: iodata(), + Reason :: not_owner | inet:posix(). +%%% +send(Socket, Destination, Packet) -> + send(Socket, Destination, [], Packet). + +-spec send(Socket, Host, Port, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Host :: inet:hostname() | inet:ip_address(), + Port :: inet:port_number() | atom(), + Packet :: iodata(), + Reason :: not_owner | inet:posix(); +%%% + (Socket, Destination, AncData, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Destination :: {inet:ip_address(), inet:port_number()} | + inet:family_address(), + AncData :: inet:ancillary_data(), + Packet :: iodata(), + Reason :: not_owner | inet:posix(); +%%% + (Socket, Destination, PortZero, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Destination :: {inet:ip_address(), inet:port_number()} | + inet:family_address(), + PortZero :: inet:port_number(), Packet :: iodata(), Reason :: not_owner | inet:posix(). +%%% +send(S, {_,_} = Destination, PortZero = AncData, Packet) when is_port(S) -> + %% Destination is {Family,Addr} | {IP,Port}, + %% so it is complete - argument PortZero is redundant + if + PortZero =:= 0 -> + case inet_db:lookup_socket(S) of + {ok, Mod} -> + Mod:send(S, Destination, [], Packet); + Error -> + Error + end; + is_integer(PortZero) -> + %% Redundant PortZero; must be 0 + {error, einval}; + is_list(AncData) -> + case inet_db:lookup_socket(S) of + {ok, Mod} -> + Mod:send(S, Destination, AncData, Packet); + Error -> + Error + end + end; +send(S, Host, Port, Packet) when is_port(S) -> + send(S, Host, Port, [], Packet). -send(S, Address, Port, Packet) when is_port(S) -> +-spec send(Socket, Host, Port, AncData, Packet) -> ok | {error, Reason} when + Socket :: socket(), + Host :: inet:hostname() | inet:ip_address() | inet:local_address(), + Port :: inet:port_number() | atom(), + AncData :: inet:ancillary_data(), + Packet :: iodata(), + Reason :: not_owner | inet:posix(). +%%% +send(S, Host, Port, AncData, Packet) + when is_port(S), is_list(AncData) -> case inet_db:lookup_socket(S) of {ok, Mod} -> - case Mod:getaddr(Address) of + case Mod:getaddr(Host) of {ok,IP} -> case Mod:getserv(Port) of - {ok,UP} -> Mod:send(S, IP, UP, Packet); + {ok,P} -> Mod:send(S, {IP,P}, AncData, Packet); {error,einval} -> exit(badarg); Error -> Error end; @@ -149,6 +209,7 @@ send(S, Address, Port, Packet) when is_port(S) -> Error end. +%% Connected send send(S, Packet) when is_port(S) -> case inet_db:lookup_socket(S) of {ok, Mod} -> diff --git a/lib/kernel/src/inet.erl b/lib/kernel/src/inet.erl index 9f22eb6aaa..24aff83fbd 100644 --- a/lib/kernel/src/inet.erl +++ b/lib/kernel/src/inet.erl @@ -75,7 +75,8 @@ -export_type([address_family/0, socket_protocol/0, hostent/0, hostname/0, ip4_address/0, ip6_address/0, ip_address/0, port_number/0, - local_address/0, socket_address/0, returned_non_ip_address/0, + family_address/0, local_address/0, + socket_address/0, returned_non_ip_address/0, socket_setopt/0, socket_getopt/0, ancillary_data/0, posix/0, socket/0, stat_option/0]). %% imports @@ -100,11 +101,16 @@ 0..65535,0..65535,0..65535,0..65535}. -type ip_address() :: ip4_address() | ip6_address(). -type port_number() :: 0..65535. --type local_address() :: {local, File :: binary() | string()}. +-type family_address() :: inet_address() | inet6_address() | local_address(). +-type inet_address() :: + {'inet', {ip4_address() | 'any' | 'loopback', port_number()}}. +-type inet6_address() :: + {'inet6', {ip6_address() | 'any' | 'loopback', port_number()}}. +-type local_address() :: {'local', File :: binary() | string()}. -type returned_non_ip_address() :: - {local, binary()} | - {unspec, <<>>} | - {undefined, any()}. + {'local', binary()} | + {'unspec', <<>>} | + {'undefined', any()}. -type posix() :: 'eaddrinuse' | 'eaddrnotavail' | 'eafnosupport' | 'ealready' | 'econnaborted' | 'econnrefused' | 'econnreset' | @@ -1639,6 +1645,7 @@ fmt_addr({ok,Addr}, Proto) -> {{0,0,0,0,0,0,0,0},Port} -> "*:" ++ fmt_port(Port, Proto); {{127,0,0,1},Port} -> "localhost:" ++ fmt_port(Port, Proto); {{0,0,0,0,0,0,0,1},Port} -> "localhost:" ++ fmt_port(Port, Proto); + {local, Path} -> "local:" ++ binary_to_list(Path); {IP,Port} -> inet_parse:ntoa(IP) ++ ":" ++ fmt_port(Port, Proto) end. diff --git a/lib/kernel/src/inet6_udp.erl b/lib/kernel/src/inet6_udp.erl index 71db0357cd..cb95a69798 100644 --- a/lib/kernel/src/inet6_udp.erl +++ b/lib/kernel/src/inet6_udp.erl @@ -65,16 +65,25 @@ open(Port, Opts) -> {ok, _} -> exit(badarg) end. -send(S, Addr = {A,B,C,D,E,F,G,H}, P, Data) - when ?ip6(A,B,C,D,E,F,G,H), ?port(P) -> - prim_inet:sendto(S, Addr, P, Data). +send(S, {A,B,C,D,E,F,G,H} = IP, Port, Data) + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port) -> + prim_inet:sendto(S, {IP, Port}, [], Data); +send(S, {{A,B,C,D,E,F,G,H}, Port} = Addr, AncData, Data) + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Addr, AncData, Data); +send(S, {?FAMILY, {{A,B,C,D,E,F,G,H}, Port}} = Address, AncData, Data) + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Address, AncData, Data); +send(S, {?FAMILY, {loopback, Port}} = Address, AncData, Data) + when ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Address, AncData, Data). send(S, Data) -> - prim_inet:sendto(S, {0,0,0,0,0,0,0,0}, 0, Data). + prim_inet:sendto(S, {any, 0}, [], Data). -connect(S, Addr = {A,B,C,D,E,F,G,H}, P) - when ?ip6(A,B,C,D,E,F,G,H), ?port(P) -> - prim_inet:connect(S, Addr, P). +connect(S, Addr = {A,B,C,D,E,F,G,H}, Port) + when ?ip6(A,B,C,D,E,F,G,H), ?port(Port) -> + prim_inet:connect(S, Addr, Port). recv(S, Len) -> prim_inet:recvfrom(S, Len). diff --git a/lib/kernel/src/inet_udp.erl b/lib/kernel/src/inet_udp.erl index 1e624b9e90..083059a2dc 100644 --- a/lib/kernel/src/inet_udp.erl +++ b/lib/kernel/src/inet_udp.erl @@ -66,16 +66,25 @@ open(Port, Opts) -> {ok, _} -> exit(badarg) end. -send(S, {A,B,C,D} = Addr, P, Data) - when ?ip(A,B,C,D), ?port(P) -> - prim_inet:sendto(S, Addr, P, Data). +send(S, {A,B,C,D} = IP, Port, Data) + when ?ip(A,B,C,D), ?port(Port) -> + prim_inet:sendto(S, {IP, Port}, [], Data); +send(S, {{A,B,C,D}, Port} = Addr, AncData, Data) + when ?ip(A,B,C,D), ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Addr, AncData, Data); +send(S, {?FAMILY, {{A,B,C,D}, Port}} = Address, AncData, Data) + when ?ip(A,B,C,D), ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Address, AncData, Data); +send(S, {?FAMILY, {loopback, Port}} = Address, AncData, Data) + when ?port(Port), is_list(AncData) -> + prim_inet:sendto(S, Address, AncData, Data). send(S, Data) -> - prim_inet:sendto(S, {0,0,0,0}, 0, Data). + prim_inet:sendto(S, {any, 0}, [], Data). -connect(S, Addr = {A,B,C,D}, P) - when ?ip(A,B,C,D), ?port(P) -> - prim_inet:connect(S, Addr, P). +connect(S, Addr = {A,B,C,D}, Port) + when ?ip(A,B,C,D), ?port(Port) -> + prim_inet:connect(S, Addr, Port). recv(S, Len) -> prim_inet:recvfrom(S, Len). diff --git a/lib/kernel/src/kernel.app.src b/lib/kernel/src/kernel.app.src index 8fe6bdd1ca..c2ff6b63e9 100644 --- a/lib/kernel/src/kernel.app.src +++ b/lib/kernel/src/kernel.app.src @@ -74,6 +74,7 @@ logger_simple_h, logger_std_h, logger_sup, + net, net_adm, net_kernel, os, diff --git a/lib/kernel/src/kernel.erl b/lib/kernel/src/kernel.erl index bfa091a036..c8c631ab23 100644 --- a/lib/kernel/src/kernel.erl +++ b/lib/kernel/src/kernel.erl @@ -116,7 +116,7 @@ init([]) -> restart => temporary, shutdown => 2000, type => supervisor, - modules => [user_sup]}, + modules => [standard_error]}, User = #{id => user, start => {user_sup, start, []}, @@ -141,7 +141,7 @@ init([]) -> modules => [logger_sup]}, case init:get_argument(mode) of - {ok, [["minimal"]]} -> + {ok, [["minimal"]|_]} -> {ok, {SupFlags, [Code, File, StdError, User, LoggerSup, Config, RefC, SafeSup]}}; _ -> diff --git a/lib/kernel/src/local_udp.erl b/lib/kernel/src/local_udp.erl index 481a8c4910..933e56228b 100644 --- a/lib/kernel/src/local_udp.erl +++ b/lib/kernel/src/local_udp.erl @@ -70,11 +70,13 @@ open(0, Opts) -> {ok, _} -> exit(badarg) end. -send(S, Addr = {?FAMILY,_}, 0, Data) -> - prim_inet:sendto(S, Addr, 0, Data). +send(S, {?FAMILY,_} = Addr, 0, Data) -> + prim_inet:sendto(S, Addr, [], Data); +send(S, {?FAMILY,_} = Addr, AncData, Data) when is_list(AncData) -> + prim_inet:sendto(S, Addr, AncData, Data). %% send(S, Data) -> - prim_inet:sendto(S, {?FAMILY,<<>>}, 0, Data). + prim_inet:sendto(S, {?FAMILY,<<>>}, [], Data). connect(S, Addr = {?FAMILY,_}, 0) -> prim_inet:connect(S, Addr, 0). diff --git a/lib/kernel/src/logger_std_h.erl b/lib/kernel/src/logger_std_h.erl index c8f1acfca4..2b078ef091 100644 --- a/lib/kernel/src/logger_std_h.erl +++ b/lib/kernel/src/logger_std_h.erl @@ -170,9 +170,11 @@ check_h_config(_Type,[]) -> ok. normalize_config(#{type:={file,File}}=HConfig) -> - HConfig#{type=>file,file=>File}; + normalize_config(HConfig#{type=>file,file=>File}); normalize_config(#{type:={file,File,Modes}}=HConfig) -> - HConfig#{type=>file,file=>File,modes=>Modes}; + normalize_config(HConfig#{type=>file,file=>File,modes=>Modes}); +normalize_config(#{file:=File}=HConfig) -> + HConfig#{file=>filename:absname(File)}; normalize_config(HConfig) -> HConfig. @@ -188,7 +190,7 @@ merge_default_config(Name,Type,HConfig) -> get_default_config(Name,file) -> #{type => file, - file => atom_to_list(Name), + file => filename:absname(atom_to_list(Name)), modes => [raw,append], file_check => 0, max_no_bytes => infinity, diff --git a/lib/kernel/src/net.erl b/lib/kernel/src/net.erl new file mode 100644 index 0000000000..b8ffa64043 --- /dev/null +++ b/lib/kernel/src/net.erl @@ -0,0 +1,324 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(net). + +%% We should really ifdef this module depending on if we actually built +%% the system with esock support (socket and prim_net), but our doc-building +%% can't handle the "variables" we need (USE_ESOCK). So instead, we just +%% leave everything hanging... +%% If one of the "hanging" functions is called when esock has been disabled, +%% the function will through a 'notsup' error (erlang:error/1). + +%% Administrative and utility functions +-export([ + info/0, + command/1 + ]). + +-export([ + gethostname/0, + getnameinfo/1, getnameinfo/2, + getaddrinfo/1, getaddrinfo/2, + + if_name2index/1, + if_index2name/1, + if_names/0 + ]). + +%% Deprecated functions from the "old" net module +-export([call/4, + cast/4, + broadcast/3, + ping/1, + relay/1, + sleep/1]). + +%% Should we define these here or refer to the prim_net module +-export_type([ + address_info/0, + name_info/0, + + name_info_flags/0, + name_info_flag/0, + name_info_flag_ext/0, + + network_interface_name/0, + network_interface_index/0 + ]). + + +-deprecated({call, 4, eventually}). +-deprecated({cast, 4, eventually}). +-deprecated({broadcast, 3, eventually}). +-deprecated({ping, 1, eventually}). +-deprecated({relay, 1, eventually}). +-deprecated({sleep, 1, eventually}). + + +-type name_info_flags() :: [name_info_flag()|name_info_flag_ext()]. +-type name_info_flag() :: namereqd | + dgram | + nofqdn | + numerichost | + nomericserv. +-type name_info_flag_ext() :: idn | + idna_allow_unassigned | + idna_use_std3_ascii_rules. +-type name_info() :: #{host := string(), + service := string()}. +-type address_info() :: #{family := socket:domain(), + socktype := socket:type(), + protocol := socket:protocol(), + address := socket:sockaddr()}. +-type network_interface_name() :: string(). +-type network_interface_index() :: non_neg_integer(). + + +%% =========================================================================== +%% +%% D E P R E C A T E D F U N C T I O N S +%% +%% =========================================================================== + +call(N,M,F,A) -> rpc:call(N,M,F,A). +cast(N,M,F,A) -> rpc:cast(N,M,F,A). +broadcast(M,F,A) -> rpc:eval_everywhere(M,F,A). +ping(Node) -> net_adm:ping(Node). +sleep(T) -> receive after T -> ok end. +relay(X) -> slave:relay(X). + + +%% =========================================================================== +%% +%% Administrative and utility API +%% +%% =========================================================================== + +-spec info() -> list(). + +-ifdef(USE_ESOCK). +info() -> + prim_net:info(). +-else. +-dialyzer({nowarn_function, info/0}). +info() -> + erlang:error(notsup). +-endif. + + +-spec command(Cmd :: term()) -> term(). + +-ifdef(USE_ESOCK). +command(Cmd) -> + prim_net:command(Cmd). +-else. +-dialyzer({nowarn_function, command/1}). +command(_Cmd) -> + erlang:error(notsup). +-endif. + + + +%% =========================================================================== +%% +%% The proper net API +%% +%% =========================================================================== + +%% =========================================================================== +%% +%% gethostname - Get the name of the current host. +%% +%% + +-spec gethostname() -> {ok, HostName} | {error, Reason} when + HostName :: string(), + Reason :: term(). + +-ifdef(USE_ESOCK). +gethostname() -> + prim_net:gethostname(). +-else. +-dialyzer({nowarn_function, gethostname/0}). +gethostname() -> + erlang:error(notsup). +-endif. + + +%% =========================================================================== +%% +%% getnameinfo - Address-to-name translation in protocol-independent manner. +%% +%% + +-spec getnameinfo(SockAddr) -> {ok, Info} | {error, Reason} when + SockAddr :: socket:sockaddr(), + Info :: name_info(), + Reason :: term(). + +getnameinfo(SockAddr) -> + getnameinfo(SockAddr, undefined). + +-spec getnameinfo(SockAddr, Flags) -> {ok, Info} | {error, Reason} when + SockAddr :: socket:sockaddr(), + Flags :: name_info_flags() | undefined, + Info :: name_info(), + Reason :: term(). + +-ifdef(USE_ESOCK). +getnameinfo(SockAddr, [] = _Flags) -> + getnameinfo(SockAddr, undefined); +getnameinfo(#{family := Fam, addr := _Addr} = SockAddr, Flags) + when ((Fam =:= inet) orelse (Fam =:= inet6)) andalso + (is_list(Flags) orelse (Flags =:= undefined)) -> + prim_net:getnameinfo(socket:ensure_sockaddr(SockAddr), Flags); +getnameinfo(#{family := Fam, path := _Path} = SockAddr, Flags) + when (Fam =:= local) andalso (is_list(Flags) orelse (Flags =:= undefined)) -> + prim_net:getnameinfo(SockAddr, Flags). +-else. +-dialyzer({nowarn_function, getnameinfo/2}). +getnameinfo(SockAddr, [] = _Flags) -> + getnameinfo(SockAddr, undefined); +getnameinfo(#{family := Fam, addr := _Addr} = _SockAddr, Flags) + when ((Fam =:= inet) orelse (Fam =:= inet6)) andalso + (is_list(Flags) orelse (Flags =:= undefined)) -> + erlang:error(notsup); +getnameinfo(#{family := Fam, path := _Path} = _SockAddr, Flags) + when (Fam =:= local) andalso (is_list(Flags) orelse (Flags =:= undefined)) -> + erlang:error(notsup). +-endif. + + +%% =========================================================================== +%% +%% getaddrinfo - Network address and service translation +%% +%% There is also a "hint" argument that we "at some point" should implement. + +-spec getaddrinfo(Host) -> {ok, Info} | {error, Reason} when + Host :: string(), + Info :: [address_info()], + Reason :: term(). + +getaddrinfo(Host) when is_list(Host) -> + getaddrinfo(Host, undefined). + + +-spec getaddrinfo(Host, undefined) -> {ok, Info} | {error, Reason} when + Host :: string(), + Info :: [address_info()], + Reason :: term() + ; (undefined, Service) -> {ok, Info} | {error, Reason} when + Service :: string(), + Info :: [address_info()], + Reason :: term() + ; (Host, Service) -> {ok, Info} | {error, Reason} when + Host :: string(), + Service :: string(), + Info :: [address_info()], + Reason :: term(). + +-ifdef(USE_ESOCK). +getaddrinfo(Host, Service) + when (is_list(Host) orelse (Host =:= undefined)) andalso + (is_list(Service) orelse (Service =:= undefined)) andalso + (not ((Service =:= undefined) andalso (Host =:= undefined))) -> + prim_net:getaddrinfo(Host, Service). +-else. +-dialyzer({nowarn_function, getaddrinfo/2}). +getaddrinfo(Host, Service) + when (is_list(Host) orelse (Host =:= undefined)) andalso + (is_list(Service) orelse (Service =:= undefined)) andalso + (not ((Service =:= undefined) andalso (Host =:= undefined))) -> + erlang:error(notsup). +-endif. + + + + +%% =========================================================================== +%% +%% if_name2index - Mappings between network interface names and indexes: +%% name -> idx +%% +%% + +-spec if_name2index(Name) -> {ok, Idx} | {error, Reason} when + Name :: network_interface_name(), + Idx :: network_interface_index(), + Reason :: term(). + +-ifdef(USE_ESOCK). +if_name2index(If) when is_list(If) -> + prim_net:if_name2index(If). +-else. +-dialyzer({nowarn_function, if_name2index/1}). +if_name2index(If) when is_list(If) -> + erlang:error(notsup). +-endif. + + + +%% =========================================================================== +%% +%% if_index2name - Mappings between network interface index and names: +%% idx -> name +%% +%% + +-spec if_index2name(Idx) -> {ok, Name} | {error, Reason} when + Idx :: network_interface_index(), + Name :: network_interface_name(), + Reason :: term(). + +-ifdef(USE_ESOCK). +if_index2name(Idx) when is_integer(Idx) -> + prim_net:if_index2name(Idx). +-else. +-dialyzer({nowarn_function, if_index2name/1}). +if_index2name(Idx) when is_integer(Idx) -> + erlang:error(notsup). +-endif. + + + +%% =========================================================================== +%% +%% if_names - Get network interface names and indexes +%% +%% + +-spec if_names() -> Names | {error, Reason} when + Names :: [{Idx, If}], + Idx :: network_interface_index(), + If :: network_interface_name(), + Reason :: term(). + +-ifdef(USE_ESOCK). +if_names() -> + prim_net:if_names(). +-else. +-dialyzer({nowarn_function, if_names/0}). +if_names() -> + erlang:error(notsup). +-endif. + + diff --git a/lib/kernel/src/seq_trace.erl b/lib/kernel/src/seq_trace.erl index 4f9d7b3e5c..f0bd1fabe9 100644 --- a/lib/kernel/src/seq_trace.erl +++ b/lib/kernel/src/seq_trace.erl @@ -59,7 +59,7 @@ set_token({Flags,Label,Serial,_From,Lastcnt}) -> F = decode_flags(Flags), set_token2([{label,Label},{serial,{Lastcnt, Serial}} | F]). --spec set_token(Component, Val) -> {Component, OldVal} when +-spec set_token(Component, Val) -> OldVal when Component :: component(), Val :: value(), OldVal :: value(). diff --git a/lib/kernel/test/code_SUITE.erl b/lib/kernel/test/code_SUITE.erl index 4f0847084f..6b133f8d6b 100644 --- a/lib/kernel/test/code_SUITE.erl +++ b/lib/kernel/test/code_SUITE.erl @@ -41,7 +41,7 @@ big_boot_embedded/1, module_status/1, native_early_modules/1, get_mode/1, - normalized_paths/1]). + normalized_paths/1, mult_embedded_flags/1]). -export([init_per_testcase/2, end_per_testcase/2, init_per_suite/1, end_per_suite/1]). @@ -72,7 +72,8 @@ all() -> on_load_purge, on_load_self_call, on_load_pending, on_load_deleted, module_status, - big_boot_embedded, native_early_modules, get_mode, normalized_paths]. + big_boot_embedded, native_early_modules, get_mode, normalized_paths, + mult_embedded_flags]. %% These need to run in order groups() -> [{sequence, [sequence], [on_load_update, @@ -354,7 +355,7 @@ load_abs(Config) when is_list(Config) -> ensure_loaded(Config) when is_list(Config) -> {module, lists} = code:ensure_loaded(lists), case init:get_argument(mode) of - {ok, [["embedded"]]} -> + {ok, [["embedded"] | _]} -> {error, embedded} = code:ensure_loaded(code_b_test), {error, badarg} = code:ensure_loaded(34), ok; @@ -1836,6 +1837,28 @@ do_normalized_paths([M|Ms]) -> do_normalized_paths([]) -> ok. +%% Make sure that the extra -mode flags are ignored +mult_embedded_flags(_Config) -> + Modes = [{" -mode embedded", embedded}, + {" -mode interactive", interactive}, + {" -mode invalid", interactive}], + + [ begin + {ArgMode, ExpectedMode} = Mode, + {ok, Node} = start_node(mode_test, ArgMode), + ExpectedMode = rpc:call(Node, code, get_mode, []), + true = stop_node(Node) + end || Mode <- Modes], + + [ begin + {ArgIgnoredMode, _} = IgnoredMode, + {ArgRelevantMode, ExpectedMode} = RelevantMode, + {ok, Node} = start_node(mode_test, ArgRelevantMode ++ ArgIgnoredMode), + ExpectedMode = rpc:call(Node, code, get_mode, []), + true = stop_node(Node) + end || IgnoredMode <- Modes, RelevantMode <- Modes], + ok. + %% Test that module_status/1 behaves as expected module_status(_Config) -> case test_server:is_cover() of diff --git a/lib/kernel/test/gen_tcp_misc_SUITE.erl b/lib/kernel/test/gen_tcp_misc_SUITE.erl index edf30448c4..de87bd9472 100644 --- a/lib/kernel/test/gen_tcp_misc_SUITE.erl +++ b/lib/kernel/test/gen_tcp_misc_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2018. All Rights Reserved. +%% Copyright Ericsson AB 1998-2019. 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. @@ -25,6 +25,7 @@ init_per_group/2,end_per_group/2, controlling_process/1, controlling_process_self/1, no_accept/1, close_with_pending_output/1, active_n/1, + active_n_closed/1, data_before_close/1, iter_max_socks/0, iter_max_socks/1, get_status/1, @@ -36,7 +37,8 @@ show_econnreset_passive/1, econnreset_after_sync_send/1, econnreset_after_async_send_active/1, econnreset_after_async_send_active_once/1, - econnreset_after_async_send_passive/1, linger_zero/1, + econnreset_after_async_send_passive/1, + linger_zero/1, linger_zero_sndbuf/1, default_options/1, http_bad_packet/1, busy_send/1, busy_disconnect_passive/1, busy_disconnect_active/1, fill_sendq/1, partial_recv_and_close/1, @@ -73,14 +75,15 @@ suite() -> all() -> [controlling_process, controlling_process_self, no_accept, close_with_pending_output, data_before_close, - iter_max_socks, passive_sockets, active_n, + iter_max_socks, passive_sockets, active_n, active_n_closed, accept_closed_by_other_process, otp_3924, closed_socket, shutdown_active, shutdown_passive, shutdown_pending, show_econnreset_active, show_econnreset_active_once, show_econnreset_passive, econnreset_after_sync_send, econnreset_after_async_send_active, econnreset_after_async_send_active_once, - econnreset_after_async_send_passive, linger_zero, + econnreset_after_async_send_passive, + linger_zero, linger_zero_sndbuf, default_options, http_bad_packet, busy_send, busy_disconnect_passive, busy_disconnect_active, fill_sendq, partial_recv_and_close, @@ -1356,7 +1359,42 @@ linger_zero(Config) when is_list(Config) -> ok = gen_tcp:close(Client), ok = ct:sleep(1), undefined = erlang:port_info(Client, connected), - {error, econnreset} = gen_tcp:recv(S, PayloadSize). + {error, econnreset} = gen_tcp:recv(S, PayloadSize), + ok. + + +linger_zero_sndbuf(Config) when is_list(Config) -> + %% All the econnreset tests will prove that {linger, {true, 0}} aborts + %% a connection when the driver queue is empty. We will test here + %% that it also works when the driver queue is not empty + %% and the linger zero option is set on the listen socket. + {OS, _} = os:type(), + {ok, Listen} = + gen_tcp:listen(0, [{active, false}, + {recbuf, 4096}, + {show_econnreset, true}, + {linger, {true, 0}}]), + {ok, Port} = inet:port(Listen), + {ok, Client} = + gen_tcp:connect(localhost, Port, + [{active, false}, + {sndbuf, 4096}]), + {ok, Server} = gen_tcp:accept(Listen), + ok = gen_tcp:close(Listen), + PayloadSize = 1024 * 1024, + Payload = binary:copy(<<"0123456789ABCDEF">>, 256 * 1024), % 1 MB + ok = gen_tcp:send(Server, Payload), + case erlang:port_info(Server, queue_size) of + {queue_size, N} when N > 0 -> ok; + {queue_size, 0} when OS =:= win32 -> ok; + {queue_size, 0} = T -> ct:fail(T) + end, + {ok, [{linger, {true, 0}}]} = inet:getopts(Server, [linger]), + ok = gen_tcp:close(Server), + ok = ct:sleep(1), + undefined = erlang:port_info(Server, connected), + {error, closed} = gen_tcp:recv(Client, PayloadSize), + ok. %% Thanks to Luke Gorrie. Tests for a very specific problem with @@ -1981,10 +2019,10 @@ recvtclass(_Config) -> %% platforms - change {unix,_} to false? %% pktoptions is not supported for IPv4 -recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0}); recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); %% Using the option returns einval, so it is not implemented. -recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +recvtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% Does not return any value - not implemented for pktoptions recvtos_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0}); @@ -1993,10 +2031,10 @@ recvtos_ok({unix,_}, _) -> true; recvtos_ok(_, _) -> false. %% pktoptions is not supported for IPv4 -recvttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +recvttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0}); recvttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); %% Using the option returns einval, so it is not implemented. -recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +recvttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); recvttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% Does not return any value - not implemented for pktoptions recvttl_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,7,0}); @@ -2005,11 +2043,11 @@ recvttl_ok({unix,_}, _) -> true; recvttl_ok(_, _) -> false. %% pktoptions is not supported for IPv6 -recvtclass_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +recvtclass_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0}); recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); recvtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% Using the option returns einval, so it is not implemented. -recvtclass_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {11,2,0}); +recvtclass_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); %% Does not return any value - not implemented for pktoptions recvtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {3,1,0}); %% @@ -2186,18 +2224,19 @@ collect_accepts(N,Tmo) -> A = millis(), receive {accepted,P,Msg} -> - [{P,Msg}] ++ collect_accepts(N-1,Tmo-(millis() - A)) + NextN = if N =:= infinity -> N; true -> N - 1 end, + [{P,Msg}] ++ collect_accepts(NextN, Tmo - (millis()-A)) after Tmo -> [] end. -define(EXPECT_ACCEPTS(Pattern,N,Timeout), (fun() -> - case collect_accepts(if N =:= infinity -> -1; true -> N end,Timeout) of + case collect_accepts((N), (Timeout)) of Pattern -> ok; - Other -> - {error,{unexpected,{Other,process_info(self(),messages)}}} + Other__ -> + {error,{unexpected,{Other__,process_info(self(),messages)}}} end end)()). @@ -2582,7 +2621,51 @@ active_once_closed(Config) when is_list(Config) -> ok = inet:setopts(A,[{active,once}]), ok = receive {tcp_closed, A} -> ok after 1000 -> error end end)(). - + +%% Check that active n and tcp_close messages behave as expected. +active_n_closed(Config) when is_list(Config) -> + {ok, L} = gen_tcp:listen(0, [binary, {active, false}]), + + P = self(), + + {ok,Port} = inet:port(L), + + spawn_link(fun() -> + Payload = <<0:50000/unit:8>>, + Cnt = 10000, + P ! {size,Cnt * byte_size(Payload)}, + {ok, S} = gen_tcp:connect("localhost", Port, [binary, {active, false}]), + _ = [gen_tcp:send(S, Payload) || _ <- lists:seq(1, Cnt)], + gen_tcp:close(S) + end), + + receive {size,SendSize} -> SendSize end, + {ok, S} = gen_tcp:accept(L), + inet:setopts(S, [{active, 10}]), + RecvSize = + (fun Server(Size) -> + receive + {tcp, S, Bin} -> + Server(byte_size(Bin) + Size); + {tcp_closed, S} -> + Size; + {tcp_passive, S} -> + inet:setopts(S, [{active, 10}]), + Server(Size); + Msg -> + io:format("~p~n", [Msg]), + Server(Size) + end + end)(0), + + gen_tcp:close(L), + + if SendSize =:= RecvSize -> + ok; + true -> + ct:fail("Send and Recv size not equal: ~p ~p",[SendSize, RecvSize]) + end. + %% Test the send_timeout socket option. send_timeout(Config) when is_list(Config) -> Dir = filename:dirname(code:which(?MODULE)), diff --git a/lib/kernel/test/gen_udp_SUITE.erl b/lib/kernel/test/gen_udp_SUITE.erl index af9985de45..70d8caf478 100644 --- a/lib/kernel/test/gen_udp_SUITE.erl +++ b/lib/kernel/test/gen_udp_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2018. All Rights Reserved. +%% Copyright Ericsson AB 1998-2019. 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. @@ -37,6 +37,7 @@ buffer_size/1, binary_passive_recv/1, max_buffer_size/1, bad_address/1, read_packets/1, open_fd/1, connect/1, implicit_inet6/1, recvtos/1, recvtosttl/1, recvttl/1, recvtclass/1, + sendtos/1, sendtosttl/1, sendttl/1, sendtclass/1, local_basic/1, local_unbound/1, local_fdopen/1, local_fdopen_unbound/1, local_abstract/1]). @@ -49,6 +50,7 @@ all() -> bad_address, read_packets, open_fd, connect, implicit_inet6, active_n, recvtos, recvtosttl, recvttl, recvtclass, + sendtos, sendtosttl, sendttl, sendtclass, {group, local}]. groups() -> @@ -312,7 +314,6 @@ read_packets(Config) when is_list(Config) -> {ok,R} = gen_udp:open(0, [{read_packets,N1}]), {ok,RP} = inet:port(R), {ok,Node} = start_node(gen_udp_SUITE_read_packets), - Die = make_ref(), %% {V1, Trace1} = read_packets_test(R, RP, Msgs, Node), {ok,[{read_packets,N1}]} = inet:getopts(R, [read_packets]), @@ -324,7 +325,7 @@ read_packets(Config) when is_list(Config) -> stop_node(Node), ct:log("N1=~p, V1=~p vs N2=~p, V2=~p",[N1,V1,N2,V2]), - dump_terms(Config, "trace1.terms", Trace2), + dump_terms(Config, "trace1.terms", Trace1), dump_terms(Config, "trace2.terms", Trace2), %% Because of the inherit racy-ness of the feature it is @@ -348,15 +349,6 @@ dump_terms(Config, Name, Terms) -> file:write_file(FName, term_to_binary(Terms)), ct:log("Logged terms to ~s",[FName]). -infinite_loop(Die) -> - receive - Die -> - ok - after - 0 -> - infinite_loop(Die) - end. - read_packets_test(R, RP, Msgs, Node) -> Receiver = self(), Tracer = @@ -577,19 +569,19 @@ active_n(Config) when is_list(Config) -> recvtos(_Config) -> test_recv_opts( - inet, [{recvtos,tos,96}], + inet, [{recvtos,tos,96}], false, fun recvtos_ok/2). recvtosttl(_Config) -> test_recv_opts( - inet, [{recvtos,tos,96},{recvttl,ttl,33}], + inet, [{recvtos,tos,96},{recvttl,ttl,33}], false, fun (OSType, OSVer) -> recvtos_ok(OSType, OSVer) andalso recvttl_ok(OSType, OSVer) end). recvttl(_Config) -> test_recv_opts( - inet, [{recvttl,ttl,33}], + inet, [{recvttl,ttl,33}], false, fun recvttl_ok/2). recvtclass(_Config) -> @@ -601,15 +593,48 @@ recvtclass(_Config) -> of [_] -> test_recv_opts( - inet6, [{recvtclass,tclass,224}], + inet6, [{recvtclass,tclass,224}], false, fun recvtclass_ok/2); [] -> {skip,ipv6_not_supported,IFs} end. + +sendtos(_Config) -> + test_recv_opts( + inet, [{recvtos,tos,96}], true, + fun sendtos_ok/2). + +sendtosttl(_Config) -> + test_recv_opts( + inet, [{recvtos,tos,96},{recvttl,ttl,33}], true, + fun (OSType, OSVer) -> + sendtos_ok(OSType, OSVer) andalso sendttl_ok(OSType, OSVer) + end). + +sendttl(_Config) -> + test_recv_opts( + inet, [{recvttl,ttl,33}], true, + fun sendttl_ok/2). + +sendtclass(_Config) -> + {ok,IFs} = inet:getifaddrs(), + case + [Name || + {Name,Opts} <- IFs, + lists:member({addr,{0,0,0,0,0,0,0,1}}, Opts)] + of + [_] -> + test_recv_opts( + inet6, [{recvtclass,tclass,224}], true, + fun sendtclass_ok/2); + [] -> + {skip,ipv6_not_supported,IFs} + end. + %% These version numbers are just above the highest noted in daily tests %% where the test fails for a plausible reason, that is the lowest -%% where we can expect that the test mighe succeed, so +%% where we can expect that the test might succeed, so %% skip on platforms lower than this. %% %% On newer versions it might be fixed, but we'll see about that @@ -621,23 +646,65 @@ recvtclass(_Config) -> %% Using the option returns einval, so it is not implemented. recvtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {17,6,0}); %% Using the option returns einval, so it is not implemented. -recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,4,0}); +recvtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0}); %% Using the option returns einval, so it is not implemented. recvtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% recvtos_ok({unix,_}, _) -> true; recvtos_ok(_, _) -> false. +%% Option has no effect +recvttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +%% recvttl_ok({unix,_}, _) -> true; recvttl_ok(_, _) -> false. %% Using the option returns einval, so it is not implemented. recvtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {9,9,0}); recvtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,6,11}); +%% Option has no effect +recvtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); %% recvtclass_ok({unix,_}, _) -> true; recvtclass_ok(_, _) -> false. + +%% To send ancillary data seems to require much higher version numbers +%% than receiving it... +%% + +%% Using the option returns einval, so it is not implemented. +sendtos_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); +sendtos_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0}); +sendtos_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +sendtos_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {4,0,0}); +sendtos_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); +%% +sendtos_ok({unix,_}, _) -> true; +sendtos_ok(_, _) -> false. + +%% Using the option returns einval, so it is not implemented. +sendttl_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {19,0,0}); +sendttl_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {4,0,0}); +%% Using the option returns enoprotoopt, so it is not implemented. +sendttl_ok({unix,freebsd}, OSVer) -> not semver_lt(OSVer, {12,1,0}); +%% Option has no effect +sendttl_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +sendttl_ok({unix,openbsd}, OSVer) -> not semver_lt(OSVer, {6,6,0}); +%% +sendttl_ok({unix,_}, _) -> true; +sendttl_ok(_, _) -> false. + +%% Using the option returns einval, so it is not implemented. +sendtclass_ok({unix,darwin}, OSVer) -> not semver_lt(OSVer, {9,9,0}); +sendtclass_ok({unix,linux}, OSVer) -> not semver_lt(OSVer, {2,6,11}); +%% Option has no effect +sendtclass_ok({unix,sunos}, OSVer) -> not semver_lt(OSVer, {5,12,0}); +%% +sendtclass_ok({unix,_}, _) -> true; +sendtclass_ok(_, _) -> false. + + semver_lt({X1,Y1,Z1}, {X2,Y2,Z2}) -> if X1 > X2 -> false; @@ -650,18 +717,18 @@ semver_lt({X1,Y1,Z1}, {X2,Y2,Z2}) -> end; semver_lt(_, {_,_,_}) -> false. -test_recv_opts(Family, Spec, OSFilter) -> +test_recv_opts(Family, Spec, TestSend, OSFilter) -> OSType = os:type(), OSVer = os:version(), case OSFilter(OSType, OSVer) of true -> io:format("Os: ~p, ~p~n", [OSType,OSVer]), - test_recv_opts(Family, Spec, OSType, OSVer); + test_recv_opts(Family, Spec, TestSend, OSType, OSVer); false -> {skip,{not_supported_for_os_version,{OSType,OSVer}}} end. %% -test_recv_opts(Family, Spec, _OSType, _OSVer) -> +test_recv_opts(Family, Spec, TestSend, _OSType, _OSVer) -> Timeout = 5000, RecvOpts = [RecvOpt || {RecvOpt,_,_} <- Spec], TrueRecvOpts = [{RecvOpt,true} || {RecvOpt,_,_} <- Spec], @@ -686,16 +753,33 @@ test_recv_opts(Family, Spec, _OSType, _OSVer) -> ok = inet:setopts(S1, TrueRecvOpts_OptsVals), {ok,TrueRecvOpts_OptsVals} = inet:getopts(S1, RecvOpts ++ Opts), %% + %% S1 now has true receive options and set option values + %% {ok,S2} = gen_udp:open(0, [Family,binary,{active,true}|FalseRecvOpts]), {ok,P2} = inet:port(S2), {ok,FalseRecvOpts_OptsVals2} = inet:getopts(S2, RecvOpts ++ Opts), OptsVals2 = FalseRecvOpts_OptsVals2 -- FalseRecvOpts, %% - ok = gen_udp:send(S2, Addr, P1, <<"abcde">>), + %% S2 now has false receive options and default option values, + %% OptsVals2 contains the default option values + %% + ok = gen_udp:send(S2, {Addr,P1}, <<"abcde">>), ok = gen_udp:send(S1, Addr, P2, <<"fghij">>), + TestSend andalso + begin + ok = gen_udp:send(S2, Addr, P1, OptsVals, <<"ABCDE">>), + ok = gen_udp:send(S2, {Addr,P1}, OptsVals, <<"12345">>) + end, {ok,{_,P2,OptsVals3,<<"abcde">>}} = gen_udp:recv(S1, 0, Timeout), verify_sets_eq(OptsVals3, OptsVals2), + TestSend andalso + begin + {ok,{_,P2,OptsVals0,<<"ABCDE">>}} = gen_udp:recv(S1, 0, Timeout), + {ok,{_,P2,OptsVals1,<<"12345">>}} = gen_udp:recv(S1, 0, Timeout), + verify_sets_eq(OptsVals0, OptsVals), + verify_sets_eq(OptsVals1, OptsVals) + end, receive {udp,S2,_,P1,<<"fghij">>} -> ok; @@ -710,8 +794,16 @@ test_recv_opts(Family, Spec, _OSType, _OSVer) -> ok = inet:setopts(S2, TrueRecvOpts), {ok,TrueRecvOpts} = inet:getopts(S2, RecvOpts), %% - ok = gen_udp:send(S2, Addr, P1, <<"klmno">>), - ok = gen_udp:send(S1, Addr, P2, <<"pqrst">>), + %% S1 now has false receive options and set option values + %% + %% S2 now has true receive options and default option values + %% + ok = gen_udp:send(S2, {Addr,P1}, [], <<"klmno">>), + ok = gen_udp:send(S1, {Family,{loopback,P2}}, <<"pqrst">>), + TestSend andalso + begin + ok = gen_udp:send(S1, {Family,{loopback,P2}}, OptsVals2, <<"PQRST">>) + end, {ok,{_,P2,<<"klmno">>}} = gen_udp:recv(S1, 0, Timeout), receive {udp,S2,_,P1,OptsVals4,<<"pqrst">>} -> @@ -721,9 +813,18 @@ test_recv_opts(Family, Spec, _OSType, _OSVer) -> after Timeout -> exit(timeout) end, + TestSend andalso + receive + {udp,S2,_,P1,OptsVals5,<<"PQRST">>} -> + verify_sets_eq(OptsVals5, OptsVals2); + Other3 -> + exit({unexpected,Other3}) + after Timeout -> + exit(timeout) + end, ok = gen_udp:close(S1), ok = gen_udp:close(S2), -%% exit({{OSType,OSVer},success}), % In search for the truth +%%% exit({{_OSType,_OSVer},success}), % In search for the truth ok. verify_sets_eq(L1, L2) -> @@ -877,6 +978,10 @@ connect(Config) when is_list(Config) -> implicit_inet6(Config) when is_list(Config) -> Host = ok(inet:gethostname()), case inet:getaddr(Host, inet6) of + {ok,{16#fe80,0,0,0,_,_,_,_} = Addr} -> + {skip, + "Got link local IPv6 address: " + ++inet:ntoa(Addr)}; {ok,Addr} -> implicit_inet6(Host, Addr); {error,Reason} -> @@ -927,11 +1032,12 @@ ok({ok,V}) -> V; ok(NotOk) -> try throw(not_ok) catch - throw:Thrown:Stacktrace -> - erlang:raise( - error, {Thrown, NotOk}, tl(Stacktrace)) + throw:not_ok:Stacktrace -> + raise_error({not_ok, NotOk}, tl(Stacktrace)) end. +raise_error(Reason, Stacktrace) -> + erlang:raise(error, Reason, Stacktrace). local_filename(Tag) -> "/tmp/" ?MODULE_STRING "_" ++ os:getpid() ++ "_" ++ atom_to_list(Tag). diff --git a/lib/kernel/test/interactive_shell_SUITE.erl b/lib/kernel/test/interactive_shell_SUITE.erl index 298a364a91..173e25c520 100644 --- a/lib/kernel/test/interactive_shell_SUITE.erl +++ b/lib/kernel/test/interactive_shell_SUITE.erl @@ -23,7 +23,8 @@ init_per_group/2,end_per_group/2, get_columns_and_rows/1, exit_initial/1, job_control_local/1, job_control_remote/1, - job_control_remote_noshell/1,ctrl_keys/1]). + job_control_remote_noshell/1,ctrl_keys/1, + get_columns_and_rows_escript/1]). -export([init_per_testcase/2, end_per_testcase/2]). %% For spawn @@ -40,7 +41,8 @@ suite() -> {timetrap,{minutes,3}}]. all() -> - [get_columns_and_rows, exit_initial, job_control_local, + [get_columns_and_rows_escript,get_columns_and_rows, + exit_initial, job_control_local, job_control_remote, job_control_remote_noshell, ctrl_keys]. @@ -72,6 +74,60 @@ end_per_group(_GroupName, Config) -> -define(dbg(Data),noop). -endif. +string_to_term(Str) -> + {ok,Tokens,_EndLine} = erl_scan:string(Str ++ "."), + {ok,AbsForm} = erl_parse:parse_exprs(Tokens), + {value,Value,_Bs} = erl_eval:exprs(AbsForm, erl_eval:new_bindings()), + Value. + +run_unbuffer_escript(Rows, Columns, EScript, NoTermStdIn, NoTermStdOut) -> + DataDir = filename:join(filename:dirname(code:which(?MODULE)), "interactive_shell_SUITE_data"), + TmpFile = filename:join(DataDir, "tmp"), + ok = file:write_file(TmpFile, <<>>), + CommandModifier = + case {NoTermStdIn, NoTermStdOut} of + {false, false} -> ""; + {true, false} -> io_lib:format(" < ~s", [TmpFile]); + {false, true} -> io_lib:format(" > ~s ; cat ~s", [TmpFile, TmpFile]); + {true, true} -> io_lib:format(" > ~s < ~s ; cat ~s", [TmpFile, TmpFile, TmpFile]) + end, + Command = io_lib:format("unbuffer -p bash -c \"stty rows ~p; stty columns ~p; escript ~s ~s\"", + [Rows, Columns, EScript, CommandModifier]), + %% io:format("Command: ~s ~n", [Command]), + Out = os:cmd(Command), + %% io:format("Out: ~p ~n", [Out]), + string_to_term(Out). + +get_columns_and_rows_escript(Config) when is_list(Config) -> + ExpectUnbufferInstalled = + try + "79" = string:trim(os:cmd("unbuffer -p bash -c \"stty columns 79 ; tput cols\"")), + true + catch + _:_ -> false + end, + case ExpectUnbufferInstalled of + false -> + {skip, + "The unbuffer tool (https://core.tcl-lang.org/expect/index) does not seem to be installed.~n" + "On Ubuntu/Debian: \"sudo apt-get install expect\""}; + true -> + DataDir = filename:join(filename:dirname(code:which(?MODULE)), "interactive_shell_SUITE_data"), + IoColumnsErl = filename:join(DataDir, "io_columns.erl"), + IoRowsErl = filename:join(DataDir, "io_rows.erl"), + [ + begin + {ok, 42} = run_unbuffer_escript(99, 42, IoColumnsErl, NoTermStdIn, NoTermStdOut), + {ok, 99} = run_unbuffer_escript(99, 42, IoRowsErl, NoTermStdIn, NoTermStdOut) + end + || + {NoTermStdIn, NoTermStdOut} <- [{false, false}, {true, false}, {false, true}] + ], + {error,enotsup} = run_unbuffer_escript(99, 42, IoRowsErl, true, true), + {error,enotsup} = run_unbuffer_escript(99, 42, IoColumnsErl, true, true), + ok + end. + %% Test that the shell can access columns and rows. get_columns_and_rows(Config) when is_list(Config) -> case proplists:get_value(default_shell,Config) of diff --git a/lib/kernel/test/interactive_shell_SUITE_data/.gitignore b/lib/kernel/test/interactive_shell_SUITE_data/.gitignore new file mode 100644 index 0000000000..1c2f433de1 --- /dev/null +++ b/lib/kernel/test/interactive_shell_SUITE_data/.gitignore @@ -0,0 +1 @@ +tmp
\ No newline at end of file diff --git a/lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl b/lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl new file mode 100644 index 0000000000..32d0cf25df --- /dev/null +++ b/lib/kernel/test/interactive_shell_SUITE_data/io_columns.erl @@ -0,0 +1,6 @@ +-module(io_columns). + +-export([main/1]). + +main(_) -> + io:format("~p",[io:columns()]). diff --git a/lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl b/lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl new file mode 100644 index 0000000000..53ceb464b0 --- /dev/null +++ b/lib/kernel/test/interactive_shell_SUITE_data/io_rows.erl @@ -0,0 +1,6 @@ +-module(io_rows). + +-export([main/1]). + +main(_) -> + io:format("~p",[io:rows()]). diff --git a/lib/kernel/test/logger_std_h_SUITE.erl b/lib/kernel/test/logger_std_h_SUITE.erl index 16ab0e97fc..2b2d509860 100644 --- a/lib/kernel/test/logger_std_h_SUITE.erl +++ b/lib/kernel/test/logger_std_h_SUITE.erl @@ -132,6 +132,7 @@ all() -> bad_input, reconfig, file_opts, + relative_file_path, sync, write_failure, sync_failure, @@ -693,6 +694,54 @@ file_opts(Config) -> file_opts(cleanup, _Config) -> logger:remove_handler(?MODULE). +relative_file_path(_Config) -> + {ok,Dir} = file:get_cwd(), + AbsName1 = filename:join(Dir,?MODULE), + ok = logger:add_handler(?MODULE, + logger_std_h, + #{config => #{type=>file}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + #{cb_state := #{handler_state := #{file:=AbsName1}}} = + logger_olp:info(h_proc_name()), + {ok,#{config := #{file:=AbsName1}}} = + logger:get_handler_config(?MODULE), + ok = logger:remove_handler(?MODULE), + + RelName2 = filename:join(atom_to_list(?FUNCTION_NAME), + lists:concat([?FUNCTION_NAME,".log"])), + AbsName2 = filename:join(Dir,RelName2), + ok = logger:add_handler(?MODULE, + logger_std_h, + #{config => #{file => RelName2}, + filter_default=>log, + filters=>?DEFAULT_HANDLER_FILTERS([?MODULE]), + formatter=>{?MODULE,self()}}), + #{cb_state := #{handler_state := #{file:=AbsName2}}} = + logger_olp:info(h_proc_name()), + {ok,#{config := #{file:=AbsName2}}} = + logger:get_handler_config(?MODULE), + logger:notice(M1=?msg,?domain), + ?check(M1), + B1 = ?bin(M1), + try_read_file(AbsName2, {ok,B1}, filesync_rep_int()), + + ok = file:set_cwd(".."), + logger:notice(M2=?msg,?domain), + ?check(M2), + B20 = ?bin(M2), + B2 = <<B1/binary,B20/binary>>, + try_read_file(AbsName2, {ok,B2}, filesync_rep_int()), + + {error,_} = logger:update_handler_config(?MODULE,config,#{file=>RelName2}), + ok = logger:update_handler_config(?MODULE,config,#{file=>AbsName2}), + ok = file:set_cwd(Dir), + ok = logger:update_handler_config(?MODULE,config,#{file=>RelName2}), + ok. +relative_file_path(cleanup,_Config) -> + logger:remove_handler(?MODULE). + sync(Config) -> Dir = ?config(priv_dir,Config), diff --git a/lib/kernel/test/seq_trace_SUITE.erl b/lib/kernel/test/seq_trace_SUITE.erl index 663f910751..83a94ab087 100644 --- a/lib/kernel/test/seq_trace_SUITE.erl +++ b/lib/kernel/test/seq_trace_SUITE.erl @@ -26,6 +26,7 @@ init_per_group/2,end_per_group/2, init_per_testcase/2,end_per_testcase/2]). -export([token_set_get/1, tracer_set_get/1, print/1, + old_heap_token/1, send/1, distributed_send/1, recv/1, distributed_recv/1, trace_exit/1, distributed_exit/1, call/1, port/1, match_set_seq_token/1, gc_seq_token/1, label_capability_mismatch/1, @@ -50,6 +51,7 @@ suite() -> all() -> [token_set_get, tracer_set_get, print, send, send_literal, distributed_send, recv, distributed_recv, trace_exit, + old_heap_token, distributed_exit, call, port, match_set_seq_token, gc_seq_token, label_capability_mismatch]. @@ -149,17 +151,19 @@ tracer_set_get(Config) when is_list(Config) -> ok. print(Config) when is_list(Config) -> - lists:foreach(fun do_print/1, ?TIMESTAMP_MODES). + [do_print(TsType, Label) || TsType <- ?TIMESTAMP_MODES, + Label <- [17, "label"]]. -do_print(TsType) -> +do_print(TsType, Label) -> start_tracer(), + seq_trace:set_token(label, Label), set_token_flags([print, TsType]), - seq_trace:print(0,print1), + seq_trace:print(Label,print1), seq_trace:print(1,print2), seq_trace:print(print3), seq_trace:reset_trace(), - [{0,{print,_,_,[],print1}, Ts0}, - {0,{print,_,_,[],print3}, Ts1}] = stop_tracer(2), + [{Label,{print,_,_,[],print1}, Ts0}, + {Label,{print,_,_,[],print3}, Ts1}] = stop_tracer(2), check_ts(TsType, Ts0), check_ts(TsType, Ts1). @@ -563,6 +567,24 @@ get_port_message(Port) -> end. +%% OTP-15849 ERL-700 +%% Verify changing label on existing token when it resides on old heap. +%% Bug caused faulty ref from old to new heap. +old_heap_token(Config) when is_list(Config) -> + seq_trace:set_token(label, 1), + erlang:garbage_collect(self(), [{type, minor}]), + erlang:garbage_collect(self(), [{type, minor}]), + %% Now token tuple should be on old-heap. + %% Set a new non-literal label which should reside on new-heap. + NewLabel = {self(), "new label"}, + 1 = seq_trace:set_token(label, NewLabel), + + %% If bug, we now have a ref from old to new heap. Yet another minor gc + %% will make that a ref to deallocated memory. + erlang:garbage_collect(self(), [{type, minor}]), + {label,NewLabel} = seq_trace:get_token(label), + ok. + match_set_seq_token(doc) -> ["Tests that match spec function set_seq_token does not " diff --git a/lib/megaco/Makefile b/lib/megaco/Makefile index 1d0bb6778c..f385df6a5c 100644 --- a/lib/megaco/Makefile +++ b/lib/megaco/Makefile @@ -1,7 +1,7 @@ # # %CopyrightBegin% # -# Copyright Ericsson AB 1999-2016. All Rights Reserved. +# Copyright Ericsson AB 1999-2019. 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. @@ -206,11 +206,18 @@ $(APP_TAR_FILE): $(APP_DIR) dialyzer_plt: $(DIA_PLT) -$(DIA_PLT): +$(DIA_PLT): Makefile @echo "Building $(APPLICATION) plt file" @dialyzer --build_plt \ --output_plt $@ \ -r ../$(APPLICATION)/ebin \ + ../../lib/kernel/ebin \ + ../../lib/stdlib/ebin \ + ../../lib/runtime_tools/ebin \ + ../../lib/asn1/ebin \ + ../../lib/debugger/ebin \ + ../../lib/et/ebin \ + ../../erts/preloaded/ebin \ --output $(DIA_ANALYSIS) \ --verbose diff --git a/lib/megaco/doc/src/megaco_edist_compress.xml b/lib/megaco/doc/src/megaco_edist_compress.xml index 16443e469c..8461c59a00 100644 --- a/lib/megaco/doc/src/megaco_edist_compress.xml +++ b/lib/megaco/doc/src/megaco_edist_compress.xml @@ -43,8 +43,8 @@ <name since="">Module:encode(R, Version) -> T</name> <fsummary>Encode (compress) a megaco component.</fsummary> <type> - <v>R = megaco_message() | transaction() | action_reply() | action_request() | command_request()</v> - <v>Version = integer()</v> + <v>R = megaco_encoder:megaco_message() | megaco_encoder:transaction() | megaco_encoder:action_reply() | megaco_encoder:action_request() | megaco_encoder:command_request()</v> + <v>Version = megaco_encoder:protocol_version()</v> <v>T = term()</v> </type> <desc> @@ -57,8 +57,8 @@ <fsummary>Decode (decompress) a megaco component.</fsummary> <type> <v>T = term()</v> - <v>Version = integer()</v> - <v>R = megaco_message() | transaction() | action_reply() | action_request() | command_request()</v> + <v>Version = megaco_encoder:protocol_version()</v> + <v>R = megaco_encoder:megaco_message() | megaco_encoder:transaction() | megaco_encoder:action_reply() | megaco_encoder:action_request() | megaco_encoder:command_request()</v> </type> <desc> <p>Decompress a megaco component. </p> diff --git a/lib/megaco/doc/src/megaco_encoder.xml b/lib/megaco/doc/src/megaco_encoder.xml index cc8270440b..0632a55d48 100644 --- a/lib/megaco/doc/src/megaco_encoder.xml +++ b/lib/megaco/doc/src/megaco_encoder.xml @@ -42,7 +42,16 @@ <section> <title>DATA TYPES</title> + <note> + <p>Note that the actual definition of (some of) these records depend on + the megaco protocol version used. For instance, the + <c>'TransactionReply'</c> record + has two more fields in version 3, so a simple erlang type definition + cannot be made here. </p> + </note> <code type="none"><![CDATA[ +protocol_version() = integer() +segment_no() = integer() megaco_message() = #'MegacoMessage{}' transaction() = {transactionRequest, transaction_request()} | {transactionPending, transaction_reply()} | @@ -57,6 +66,8 @@ transaction_ack() = #'TransactionAck'{} segment_reply() = #'SegmentReply'{} action_request() = #'ActionRequest'{} action_reply() = #'ActionReply'{} +command_request() = #'CommandRequest'{} +error_desc() = #'ErrorDescriptor'{} ]]></code> <marker id="encode_message"></marker> diff --git a/lib/megaco/doc/src/megaco_user.xml b/lib/megaco/doc/src/megaco_user.xml index 198f2aa24c..56d4d51cde 100644 --- a/lib/megaco/doc/src/megaco_user.xml +++ b/lib/megaco/doc/src/megaco_user.xml @@ -165,7 +165,7 @@ protocol_version() = integer() ]]></code> <funcs> <func> <name since="">handle_connect(ConnHandle, ProtocolVersion) -> ok | error | {error,ErrorDescr}</name> - <name since="">handle_connect(ConnHandle, ProtocolVersion, Extra]) -> ok | error | {error,ErrorDescr}</name> + <name since="">handle_connect(ConnHandle, ProtocolVersion, Extra) -> ok | error | {error,ErrorDescr}</name> <fsummary>Invoked when a new connection is established</fsummary> <type> <v>ConnHandle = conn_handle()</v> diff --git a/lib/megaco/examples/simple/megaco_simple_mgc.erl b/lib/megaco/examples/simple/megaco_simple_mgc.erl index f324e17a3a..8a78262b86 100644 --- a/lib/megaco/examples/simple/megaco_simple_mgc.erl +++ b/lib/megaco/examples/simple/megaco_simple_mgc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2016. All Rights Reserved. +%% Copyright Ericsson AB 2001-2019. 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. @@ -193,15 +193,17 @@ stop(Mid) -> d("stop -> entry with~n Mid: ~p", [Mid]), Disco = fun(CH) -> d("stop -> CH: ~p", [CH]), - Reason = stopped_by_user, - Pid = megaco:conn_info(CH, control_pid), - SendMod = megaco:conn_info(CH, send_mod), + Reason = stopped_by_user, + Pid = megaco:conn_info(CH, control_pid), + SendMod = megaco:conn_info(CH, send_mod), SendHandle = megaco:conn_info(CH, send_handle), d("stop -> disconnect", []), megaco:disconnect(CH, Reason), + d("stop -> cancel", []), - megaco:cancel(CH, Reason), + megaco:cancel(CH, Reason), % see handle_disconnect + d("stop -> close transport" "~n SendMod: ~p" "~n SendHandle: ~p", [SendMod, SendHandle]), @@ -247,6 +249,7 @@ handle_disconnect(ConnHandle, ProtocolVersion, Reason) -> "~n ProtocolVersion: ~p" "~n Reason: ~p" "", [ConnHandle, ProtocolVersion, Reason]), + info_msg("handle_disconnect - cancel outstanding messages~n"), megaco:cancel(ConnHandle, Reason), % Cancel the outstanding messages ok. @@ -443,6 +446,12 @@ get_arg(Key, Args) -> %% DEBUGGING %%---------------------------------------------------------------------- +info_msg(F) -> + info_msg(F, []). +info_msg(F, A) -> + io:format("~p MGC: " ++ F ++ "~n", [self()|A]). + + d(F) -> d(F, []). diff --git a/lib/megaco/src/app/megaco.app.src b/lib/megaco/src/app/megaco.app.src index c54c80351c..5fb7273b4a 100644 --- a/lib/megaco/src/app/megaco.app.src +++ b/lib/megaco/src/app/megaco.app.src @@ -107,6 +107,7 @@ megaco_udp, megaco_udp_server, megaco_udp_sup, + megaco_user, megaco_user_default ]}, {registered, [megaco_config, megaco_monitor, diff --git a/lib/megaco/src/app/megaco.erl b/lib/megaco/src/app/megaco.erl index f0c209fd6c..9ed042401b 100644 --- a/lib/megaco/src/app/megaco.erl +++ b/lib/megaco/src/app/megaco.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. 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. @@ -94,6 +94,11 @@ %% This is for XREF -deprecated([{format_versions, 1, eventually}]). +-export_type([ + void/0 + ]). + +-type void() :: term(). -include("megaco_internal.hrl"). @@ -686,13 +691,8 @@ sys_info() -> [{arch, SysArch}, {ver, SysVer}]. os_info() -> - V = os:version(), - case os:type() of - {OsFam, OsName} -> - [{fam, OsFam}, {name, OsName}, {ver, V}]; - OsFam -> - [{fam, OsFam}, {ver, V}] - end. + {OsFam, OsName} = os:type(), + [{fam, OsFam}, {name, OsName}, {ver, os:version()}]. ms() -> ms1(). diff --git a/lib/megaco/src/binary/megaco_binary_encoder_lib.erl b/lib/megaco/src/binary/megaco_binary_encoder_lib.erl index 5e9836dc48..fdbb42c2f8 100644 --- a/lib/megaco/src/binary/megaco_binary_encoder_lib.erl +++ b/lib/megaco/src/binary/megaco_binary_encoder_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. 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. @@ -141,6 +141,7 @@ encode_transaction(_EC, T, _AsnMod, _TransMod, _Type) -> TransMod :: atom(), Type :: atom()) -> {'ok', binary()} | {'error', any()}. +-dialyzer({nowarn_function, do_encode_transaction/5}). % Future compat do_encode_transaction([native], _Trans, _AsnMod, _TransMod, binary) -> %% asn1rt:encode(AsnMod, element(1, T), T); {error, not_implemented}; @@ -173,6 +174,7 @@ do_encode_transaction(EC, _Trans, _AsnMod, _TransMod, _Type) -> TransMod :: atom(), Type :: atom()) -> {'ok', binary()} | {'error', any()}. +-dialyzer({nowarn_function, encode_action_requests/5}). % Future compat encode_action_requests([native], _ARs, _AsnMod, _TransMod, binary) -> %% asn1rt:encode(AsnMod, element(1, T), T); {error, not_implemented}; @@ -203,6 +205,7 @@ encode_action_requests(EC, _ARs, _AsnMod, _TransMod, _Type) -> TransMod :: atom(), Type :: atom()) -> {'ok', binary()} | {'error', any()}. +-dialyzer({nowarn_function, encode_action_request/5}). % Future compat encode_action_request([native], _AR, _AsnMod, _TransMod, binary) -> %% asn1rt:encode(AsnMod, element(1, T), T); {error, not_implemented}; @@ -226,6 +229,8 @@ encode_action_request(EC, _AR, _AsnMod, _TransMod, _Type) -> %% Convert a ActionReply record into a binary %% Return {ok, DeepIoList} | {error, Reason} %%---------------------------------------------------------------------- + +-dialyzer({nowarn_function, encode_action_reply/5}). % Future compat encode_action_reply([native], _ARs, _AsnMod, _TransMod, binary) -> %% asn1rt:encode(AsnMod, element(1, T), T); {error, not_implemented}; diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl index af97056d5d..f9e3fe39e3 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3a.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -1715,7 +1715,7 @@ decode_nt({event_parameter, Item}, SubItem) -> [16#00, 16#01] -> "cs" end; [16#00, 16#06] -> % Event qualert - case Item of + case SubItem of [16#00, 16#01] -> "th" end end; diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl index b543abe7c8..141225f501 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3b.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. 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. @@ -1715,7 +1715,7 @@ decode_nt({event_parameter, Item}, SubItem) -> [16#00, 16#01] -> "cs" end; [16#00, 16#06] -> % Event qualert - case Item of + case SubItem of [16#00, 16#01] -> "th" end end; diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl index 827cb3920b..b27a0be26e 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_prev3c.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. 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. @@ -1716,7 +1716,7 @@ decode_nt({event_parameter, Item}, SubItem) -> [16#00, 16#01] -> "cs" end; [16#00, 16#06] -> % Event qualert - case Item of + case SubItem of [16#00, 16#01] -> "th" end end; diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_v1.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_v1.erl index 1fba60fed6..aba64bb9f2 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_v1.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_v1.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2016. All Rights Reserved. +%% Copyright Ericsson AB 2000-2019. 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. @@ -1325,7 +1325,7 @@ decode_nt({event_parameter, Item}, SubItem) -> [16#00, 16#01] -> "cs" end; [16#00, 16#06] -> % Event qualert - case Item of + case SubItem of [16#00, 16#01] -> "th" end end; diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_v2.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_v2.erl index 45b9b32772..dd07f8b404 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_v2.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_v2.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2019. 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. @@ -1393,7 +1393,7 @@ decode_nt({event_parameter, Item}, SubItem) -> [16#00, 16#01] -> "cs" end; [16#00, 16#06] -> % Event qualert - case Item of + case SubItem of [16#00, 16#01] -> "th" end end; diff --git a/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl b/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl index f1482bc252..a8c4211235 100644 --- a/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl +++ b/lib/megaco/src/binary/megaco_binary_name_resolver_v3.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. 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. @@ -1728,7 +1728,7 @@ decode_nt({event_parameter, Item}, SubItem) -> [16#00, 16#01] -> "cs" end; [16#00, 16#06] -> % Event qualert - case Item of + case SubItem of [16#00, 16#01] -> "th" end end; diff --git a/lib/megaco/src/engine/depend.mk b/lib/megaco/src/engine/depend.mk index 96ee337e3a..ba919659db 100644 --- a/lib/megaco/src/engine/depend.mk +++ b/lib/megaco/src/engine/depend.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2003-2016. All Rights Reserved. +# Copyright Ericsson AB 2003-2019. 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. @@ -78,6 +78,10 @@ $(EBIN)/megaco_transport.$(EMULATOR): megaco_transport.erl $(EBIN)/megaco_user.$(EMULATOR): megaco_user.erl +$(EBIN)/megaco_user.$(EMULATOR): megaco_user.erl \ + ../../include/megaco.hrl \ + ../../include/megaco_message_v1.hrl + $(EBIN)/megaco_user_default.$(EMULATOR): megaco_user_default.erl \ ../../include/megaco.hrl \ ../../include/megaco_message_v1.hrl diff --git a/lib/megaco/src/engine/megaco_edist_compress.erl b/lib/megaco/src/engine/megaco_edist_compress.erl index 987a5ec717..968ab6f16e 100644 --- a/lib/megaco/src/engine/megaco_edist_compress.erl +++ b/lib/megaco/src/engine/megaco_edist_compress.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2016. All Rights Reserved. +%% Copyright Ericsson AB 2007-2019. 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. @@ -25,10 +25,21 @@ -module(megaco_edist_compress). --export([behaviour_info/1]). +-callback encode(R, Version) -> T when + R :: megaco_encoder:megaco_message() | + megaco_encoder:transaction() | + megaco_encoder:action_reply() | + megaco_encoder:action_request() | + megaco_encoder:command_request(), + Version :: megaco_encoder:protocol_version(), + T :: term(). + +-callback decode(T, Version) -> R when + T :: term(), + Version :: megaco_encoder:protocol_version() | dynamic, + R :: megaco_encoder:megaco_message() | + megaco_encoder:transaction() | + megaco_encoder:action_reply() | + megaco_encoder:action_request() | + megaco_encoder:command_request(). -behaviour_info(callbacks) -> - [{encode,2}, - {decode,2}]; -behaviour_info(_) -> - undefined. diff --git a/lib/megaco/src/engine/megaco_encoder.erl b/lib/megaco/src/engine/megaco_encoder.erl index 7ade349083..dd5a3458fc 100644 --- a/lib/megaco/src/engine/megaco_encoder.erl +++ b/lib/megaco/src/engine/megaco_encoder.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2019. 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. @@ -25,14 +25,108 @@ -module(megaco_encoder). --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{encode_message, 3}, - {decode_message, 3}, - {decode_mini_message, 3}, - {encode_transaction, 3}, - {encode_action_requests, 3}, - {encode_action_reply, 3}]; -behaviour_info(_) -> - undefined. +-export_type([ + protocol_version/0, + segment_no/0, + megaco_message/0, + transaction/0, + transaction_request/0, + transaction_pending/0, + transaction_reply/0, + transaction_response_ack/0, + transaction_ack/0, + segment_reply/0, + action_request/0, + action_reply/0, + command_request/0, + error_desc/0 + ]). + + +-include("megaco_message_internal.hrl"). + +-type protocol_version() :: integer(). +-type segment_no() :: integer(). +-type megaco_message() :: #'MegacoMessage'{}. +-type transaction() :: {transactionRequest, transaction_request()} | + {transactionPending, transaction_reply()} | + {transactionReply, transaction_pending()} | + {transactionResponseAck, transaction_response_ack()} | + {segmentReply, segment_reply()}. +-type transaction_request() :: #'TransactionRequest'{}. +-type transaction_pending() :: #'TransactionPending'{}. +%% The problem with TransactionReply is that its definition depend +%% on which version of the protocol we are using. As of version 3, +%% it has two more fields. +%% -type transaction_reply() :: #'TransactionReply'{}. +-type transaction_reply() :: {'TransactionReply', _, _} | + {'TransactionReply', _, _, _, _}. +-type transaction_response_ack() :: [transaction_ack()]. +-type transaction_ack() :: #'TransactionAck'{}. +-type segment_reply() :: #'SegmentReply'{}. +%% -type action_request() :: #'ActionRequest'{}. +-type action_request() :: {'ActionRequest', _, _, _, _}. +%% -type action_reply() :: #'ActionReply'{}. +-type action_reply() :: {'ActionReply', _, _, _}. +%% -type command_request() :: #'CommandRequest'{}. +-type command_request() :: {'CommandRequest', _, _, _}. +-type error_desc() :: #'ErrorDescriptor'{}. + +-callback encode_message(EncodingConfig, + Version, + Message) -> {ok, Bin} | Error when + EncodingConfig :: list(), + Version :: protocol_version(), + Message :: megaco_message(), + Bin :: binary(), + Error :: term(). + +-callback decode_message(EncodingConfig, + Version, + Bin) -> {ok, Message} | Error when + EncodingConfig :: list(), + Version :: protocol_version() | dynamic, + Bin :: binary(), + Message :: megaco_message(), + Error :: term(). + +-callback decode_mini_message(EncodingConfig, + Version, + Bin) -> {ok, Message} | Error when + EncodingConfig :: list(), + Version :: protocol_version() | dynamic, + Bin :: binary(), + Message :: megaco_message(), + Error :: term(). + +-callback encode_transaction(EncodingConfig, + Version, + Transaction) -> {ok, Bin} | {error, Reason} when + EncodingConfig :: list(), + Version :: protocol_version(), + Transaction :: transaction(), + Bin :: binary(), + Reason :: not_implemented | term(). + +-callback encode_action_requests(EncodingConfig, + Version, + ARs) -> {ok, Bin} | {error, Reason} when + EncodingConfig :: list(), + Version :: protocol_version(), + ARs :: [action_request()], + Bin :: binary(), + Reason :: not_implemented | term(). + +-callback encode_action_reply(EncodingConfig, + Version, + AR) -> {ok, Bin} | {error, Reason} when + EncodingConfig :: list(), + Version :: protocol_version(), + AR :: action_reply(), + Bin :: binary(), + Reason :: not_implemented | term(). + +-optional_callbacks( + [ + encode_action_reply/3 % Only used if segementation is used + ]). diff --git a/lib/megaco/src/engine/megaco_messenger.erl b/lib/megaco/src/engine/megaco_messenger.erl index 1d462b2140..2a9ecee2a7 100644 --- a/lib/megaco/src/engine/megaco_messenger.erl +++ b/lib/megaco/src/engine/megaco_messenger.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. 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. @@ -1391,7 +1391,7 @@ prepare_request(ConnData, T, Rest, AckList, ReqList, Extra) -> %% don't restart the reply_timer. ConnData2 = ConnData#conn_data{protocol_version = Version}, ?report_trace(ConnData2, - "re-send trans reply", [T | {bytes, Bin}]), + "re-send trans reply", [T, {bytes, Bin}]), case megaco_messenger_misc:send_message(ConnData2, true, Bin) of {ok, _} -> ok; diff --git a/lib/megaco/src/engine/megaco_user.erl b/lib/megaco/src/engine/megaco_user.erl new file mode 100644 index 0000000000..47fb1a119d --- /dev/null +++ b/lib/megaco/src/engine/megaco_user.erl @@ -0,0 +1,386 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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% +%% + +%% +%%------------------------------------------------------------------------- +%% Purpose: Megaco user behaviour module +%% +%% This callback functions are the default! Its possible for the user to +%% provide a arbitrary number of "extra" arguments via the user_args +%% config option. +%% So, for instance, the handle_connect/2 could instead become +%% handle_connect/4 if the user sets the user_args option to [foo, bar]. +%% This means that its impossible to define a proper behaviour. +%% So what we do here is to define a behaviour with the "default interface" +%% (the user_args option has the [] as the default value) and set them +%% all to be optional! +%%------------------------------------------------------------------------- + +-module(megaco_user). + +-export_type([ + receive_handle/0, + conn_handle/0, + megaco_timer/0 + ]). + +-include_lib("megaco/include/megaco.hrl"). +%% -include_lib("megaco/include/megaco_message_v1.hrl"). + +-type receive_handle() :: #megaco_receive_handle{}. +-type conn_handle() :: #megaco_conn_handle{}. +-type megaco_timer() :: infinity | non_neg_integer() | #megaco_incr_timer{}. + +-callback handle_connect(ConnHandle, ProtocolVersion) -> + ok | error | {error, ErrorDescr} when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ErrorDescr :: megaco_encoder:error_desc(). +-callback handle_connect(ConnHandle, ProtocolVersion, Extra) -> + ok | error | {error, ErrorDescr} when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + Extra :: term(), + ErrorDescr :: megaco_encoder:error_desc(). + +-callback handle_disconnect(ConnHandle, ProtocolVersion, Reason) -> + megaco:void() when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + Reason :: term(). + +-callback handle_syntax_error(ReceiveHandle, ProtocolVersion, DefaultED) -> + reply | {reply, ED} | no_reply | {no_reply, ED} when + ReceiveHandle :: receive_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + DefaultED :: megaco_encoder:error_desc(), + ED :: megaco_encoder:error_desc(). +-callback handle_syntax_error(ReceiveHandle, ProtocolVersion, DefaultED, Extra) -> + reply | {reply, ED} | no_reply | {no_reply, ED} when + ReceiveHandle :: receive_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + DefaultED :: megaco_encoder:error_desc(), + ED :: megaco_encoder:error_desc(), + Extra :: term(). + +-callback handle_message_error(ConnHandle, ProtocolVersion, ErrorDescr) -> + megaco:void() when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ErrorDescr :: megaco_encoder:error_desc(). +-callback handle_message_error(ConnHandle, ProtocolVersion, ErrorDescr, Extra) -> + megaco:void() when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ErrorDescr :: megaco_encoder:error_desc(), + Extra :: term(). + +-callback handle_trans_request(ConnHandle, ProtocolVersion, ActionRequests) -> + Pending | Reply | ignore_trans_request when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ActionRequests :: [megaco_encoder:action_request()], + Pending :: {pending, ReqData}, + ReqData :: term(), + Reply :: {AckAction, ActualReply} | + {AckAction, ActualReply, SendOptions}, + AckAction :: discard_ack | + {handle_ack, AckData} | + {handle_pending_ack, AckData} | + {handle_sloppy_ack, AckData}, + ActualReply :: [megaco_encoder:action_reply()] | + megaco_encoder:error_desc(), + AckData :: term(), + SendOptions :: [SendOption], + SendOption :: {reply_timer, megaco_timer()} | + {send_handle, term()} | + {protocol_version, integer()}. +-callback handle_trans_request(ConnHandle, + ProtocolVersion, + ActionRequests, + Extra) -> + Pending | Reply | ignore_trans_request when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ActionRequests :: [megaco_encoder:action_request()], + Extra :: term(), + Pending :: {pending, ReqData}, + ReqData :: term(), + Reply :: {AckAction, ActualReply} | + {AckAction, ActualReply, SendOptions}, + AckAction :: discard_ack | + {handle_ack, AckData} | + {handle_pending_ack, AckData} | + {handle_sloppy_ack, AckData}, + ActualReply :: [megaco_encoder:action_reply()] | + megaco_encoder:error_desc(), + AckData :: term(), + SendOptions :: [SendOption], + SendOption :: {reply_timer, megaco_timer()} | + {send_handle, term()} | + {protocol_version, integer()}. + +-callback handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData) -> + Reply when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ReqData :: term(), + Reply :: {AckAction, ActualReply} | + {AckAction, ActualReply, SendOptions}, + AckAction :: discard_ack | + {handle_ack, AckData} | + {handle_sloppy_ack, AckData}, + ActualReply :: [megaco_encoder:action_reply()] | + megaco_encoder:error_desc(), + AckData :: term(), + SendOptions :: [SendOption], + SendOption :: {reply_timer, megaco_timer()} | + {send_handle, term()} | + {protocol_version, megaco_encoder:protocol_version()}. +-callback handle_trans_long_request(ConnHandle, ProtocolVersion, ReqData, Extra) -> + Reply when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + ReqData :: term(), + Extra :: term(), + Reply :: {AckAction, ActualReply} | + {AckAction, ActualReply, SendOptions}, + AckAction :: discard_ack | + {handle_ack, AckData} | + {handle_sloppy_ack, AckData}, + ActualReply :: [megaco_encoder:action_reply()] | + megaco_encoder:error_desc(), + AckData :: term(), + SendOptions :: [SendOption], + SendOption :: {reply_timer, megaco_timer()} | + {send_handle, term()} | + {protocol_version, megaco_encoder:protocol_version()}. + +-callback handle_trans_reply(ConnHandle, + ProtocolVersion, + UserReply, + ReplyData) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + UserReply :: Success | Failure, + ReplyData :: term(), + Success :: {ok, Result}, + Result :: TransactionResult | SegmentResult, + TransactionResult :: [megaco_encoder:action_reply()], + SegmentResult :: {megaco_encoder:segment_no(), + LastSegment, + [megaco_encoder:action_reply()]}, + Failure :: {error, Reason} | + {error, ReplyNo, Reason}, + Reason :: TransactionReason | + SegmentReason | + UserCancelReason | + SendReason | + OtherReason, + TransactionReason :: megaco_encoder:error_desc(), + SegmentReason :: {megaco_encoder:segment_no(), + LastSegment, + megaco_encoder:error_desc()}, + OtherReason :: timeout | + {segment_timeout, MissingSegments} | + exceeded_recv_pending_limit | term(), + LastSegment :: boolean(), + MissingSegments :: [megaco_encoder:segment_no()], + UserCancelReason :: {user_cancel, ReasonForUserCancel}, + ReasonForUserCancel :: term(), + SendReason :: SendCancelledReason | SendFailedReason, + SendCancelledReason :: {send_message_cancelled, + ReasonForSendCancel}, + ReasonForSendCancel :: term(), + SendFailedReason :: {send_message_failed, ReasonForSendFailure}, + ReasonForSendFailure :: term(), + ReplyNo :: pos_integer(). +-callback handle_trans_reply(ConnHandle, + ProtocolVersion, + UserReply, + ReplyData, + Extra) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + UserReply :: Success | Failure, + ReplyData :: term(), + Extra :: term(), + Success :: {ok, Result}, + Result :: TransactionResult | SegmentResult, + TransactionResult :: [megaco_encoder:action_reply()], + SegmentResult :: {megaco_encoder:segment_no(), + LastSegment, + [megaco_encoder:action_reply()]}, + Failure :: {error, Reason} | + {error, ReplyNo, Reason}, + Reason :: TransactionReason | + SegmentReason | + UserCancelReason | + SendReason | + OtherReason, + TransactionReason :: megaco_encoder:error_desc(), + SegmentReason :: {megaco_encoder:segment_no(), + LastSegment, + megaco_encoder:error_desc()}, + OtherReason :: timeout | + {segment_timeout, MissingSegments} | + exceeded_recv_pending_limit | term(), + LastSegment :: boolean(), + MissingSegments :: [megaco_encoder:segment_no()], + UserCancelReason :: {user_cancel, ReasonForUserCancel}, + ReasonForUserCancel :: term(), + SendReason :: SendCancelledReason | SendFailedReason, + SendCancelledReason :: {send_message_cancelled, + ReasonForSendCancel}, + ReasonForSendCancel :: term(), + SendFailedReason :: {send_message_failed, ReasonForSendFailure}, + ReasonForSendFailure :: term(), + ReplyNo :: pos_integer(). + + +-callback handle_trans_ack(ConnHandle, + ProtocolVersion, + AckStatus, + AckData) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + AckStatus :: ok | {error, Reason}, + AckData :: term(), + Reason :: UserCancelReason | SendReason | OtherReason, + UserCancelReason :: {user_cancel, ReasonForUserCancel}, + ReasonForUserCancel :: term(), + SendReason :: SendCancelledReason | SendFailedReason, + SendCancelledReason :: {send_message_cancelled, ReasonForSendCancel}, + ReasonForSendCancel :: term(), + SendFailedReason :: {send_message_failed, ReasonForSendFailure}, + ReasonForSendFailure :: term(), + OtherReason :: term(). +-callback handle_trans_ack(ConnHandle, + ProtocolVersion, + AckStatus, + AckData, + Extra) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + AckStatus :: ok | {error, Reason}, + AckData :: term(), + Extra :: term(), + Reason :: UserCancelReason | SendReason | OtherReason, + UserCancelReason :: {user_cancel, ReasonForUserCancel}, + ReasonForUserCancel :: term(), + SendReason :: SendCancelledReason | SendFailedReason, + SendCancelledReason :: {send_message_cancelled, ReasonForSendCancel}, + ReasonForSendCancel :: term(), + SendFailedReason :: {send_message_failed, ReasonForSendFailure}, + ReasonForSendFailure :: term(), + OtherReason :: term(). + +-callback handle_unexpected_trans(ConnHandle, ProtocolVersion, Trans) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + Trans :: megaco_encoder:transaction_pending() | + megaco_encoder:transaction_reply() | + megaco_encoder:transaction_response_ack(). +-callback handle_unexpected_trans(ConnHandle, ProtocolVersion, Trans, Extra) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + Trans :: megaco_encoder:transaction_pending() | + megaco_encoder:transaction_reply() | + megaco_encoder:transaction_response_ack(), + Extra :: term(). + +-callback handle_trans_request_abort(ConnHandle, + ProtocolVersion, + TransNo, + Pid) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + TransNo :: integer(), + Pid :: undefined | pid(). +-callback handle_trans_request_abort(ConnHandle, + ProtocolVersion, + TransNo, + Pid, + Extra) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + TransNo :: integer(), + Pid :: undefined | pid(), + Extra :: term(). + +-callback handle_segment_reply(ConnHandle, + ProtocolVersion, + TransNo, + SegNo, + SegCompl) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + TransNo :: integer(), + SegNo :: integer(), + SegCompl :: asn1_NOVALUE | 'NULL'. +-callback handle_segment_reply(ConnHandle, + ProtocolVersion, + TransNo, + SegNo, + SegCompl, + Extra) -> + ok when + ConnHandle :: conn_handle(), + ProtocolVersion :: megaco_encoder:protocol_version(), + TransNo :: integer(), + SegNo :: megaco_encoder:segment_no(), + SegCompl :: asn1_NOVALUE | 'NULL', + Extra :: term(). + +-optional_callbacks( + [ + %% The actual number of arguments to *all* functions, + %% depend of the user_args config option. + handle_connect/2, + handle_connect/3, + handle_disconnect/3, + handle_syntax_error/3, + handle_syntax_error/4, + handle_message_error/3, + handle_message_error/4, + handle_trans_request/3, + handle_trans_request/4, + handle_trans_long_request/3, + handle_trans_long_request/4, + handle_trans_reply/4, + handle_trans_reply/5, + handle_trans_ack/4, + handle_trans_ack/5, + handle_unexpected_trans/3, + handle_unexpected_trans/4, + handle_trans_request_abort/4, + handle_trans_request_abort/5, + handle_segment_reply/5, + handle_segment_reply/6 + ]). diff --git a/lib/megaco/src/engine/modules.mk b/lib/megaco/src/engine/modules.mk index b74a096e40..a7f82c1836 100644 --- a/lib/megaco/src/engine/modules.mk +++ b/lib/megaco/src/engine/modules.mk @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 2001-2016. All Rights Reserved. +# Copyright Ericsson AB 2001-2019. 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. @@ -21,6 +21,7 @@ BEHAVIOUR_MODULES = \ megaco_edist_compress \ megaco_encoder \ + megaco_user \ megaco_transport MODULES = \ diff --git a/lib/megaco/src/text/megaco_text_gen_prev3a.hrl b/lib/megaco/src/text/megaco_text_gen_prev3a.hrl index db7507fd27..4f3c83c6c5 100644 --- a/lib/megaco/src/text/megaco_text_gen_prev3a.hrl +++ b/lib/megaco/src/text/megaco_text_gen_prev3a.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2017. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -2841,6 +2841,7 @@ enc_integer(Val, _State, Min, Max) -> enc_list(List, State) -> enc_list(List, State, fun(_S) -> ?COMMA_INDENT(_S) end, false). +-dialyzer({nowarn_function, enc_list/4}). % Future compat enc_list([], _State, _SepEncoder, _NeedsSep) -> []; enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) -> diff --git a/lib/megaco/src/text/megaco_text_gen_prev3b.hrl b/lib/megaco/src/text/megaco_text_gen_prev3b.hrl index d5f29025c8..de64f8bbdf 100644 --- a/lib/megaco/src/text/megaco_text_gen_prev3b.hrl +++ b/lib/megaco/src/text/megaco_text_gen_prev3b.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2017. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. 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. @@ -2862,6 +2862,7 @@ enc_integer(Val, _State, Min, Max) -> enc_list(List, State) -> enc_list(List, State, fun(_S) -> ?COMMA_INDENT(_S) end, false). +-dialyzer({nowarn_function, enc_list/4}). % Future compat enc_list([], _State, _SepEncoder, _NeedsSep) -> []; enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) -> diff --git a/lib/megaco/src/text/megaco_text_gen_prev3c.hrl b/lib/megaco/src/text/megaco_text_gen_prev3c.hrl index 6452be25d0..f73318161f 100644 --- a/lib/megaco/src/text/megaco_text_gen_prev3c.hrl +++ b/lib/megaco/src/text/megaco_text_gen_prev3c.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2017. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. 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. @@ -3338,6 +3338,7 @@ enc_integer(Val, _State, Min, Max) -> enc_list(List, State) -> enc_list(List, State, fun(_S) -> ?COMMA_INDENT(_S) end, false). +-dialyzer({nowarn_function, enc_list/4}). % Future compat enc_list([], _State, _SepEncoder, _NeedsSep) -> []; enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) -> diff --git a/lib/megaco/src/text/megaco_text_gen_v1.hrl b/lib/megaco/src/text/megaco_text_gen_v1.hrl index 38a0f6fd6b..6da9ea94ff 100644 --- a/lib/megaco/src/text/megaco_text_gen_v1.hrl +++ b/lib/megaco/src/text/megaco_text_gen_v1.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2016. All Rights Reserved. +%% Copyright Ericsson AB 2000-2019. 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. @@ -2303,6 +2303,7 @@ enc_integer(Val, _State, Min, Max) -> enc_list(List, State) -> enc_list(List, State, fun(_S) -> ?COMMA_INDENT(_S) end, false). +-dialyzer({nowarn_function, enc_list/4}). % Future compat enc_list([], _State, _SepEncoder, _NeedsSep) -> []; enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) -> diff --git a/lib/megaco/src/text/megaco_text_gen_v2.hrl b/lib/megaco/src/text/megaco_text_gen_v2.hrl index d9443fb2e1..23afa85800 100644 --- a/lib/megaco/src/text/megaco_text_gen_v2.hrl +++ b/lib/megaco/src/text/megaco_text_gen_v2.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2019. 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. @@ -2689,6 +2689,7 @@ enc_integer(Val, _State, Min, Max) -> enc_list(List, State) -> enc_list(List, State, fun(_S) -> ?COMMA_INDENT(_S) end, false). +-dialyzer({nowarn_function, enc_list/4}). % Future compat enc_list([], _State, _SepEncoder, _NeedsSep) -> []; enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) -> diff --git a/lib/megaco/src/text/megaco_text_gen_v3.hrl b/lib/megaco/src/text/megaco_text_gen_v3.hrl index dc1f2b9665..e440ec6651 100644 --- a/lib/megaco/src/text/megaco_text_gen_v3.hrl +++ b/lib/megaco/src/text/megaco_text_gen_v3.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. 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. @@ -3353,6 +3353,7 @@ enc_integer(Val, _State, Min, Max) -> enc_list(List, State) -> enc_list(List, State, fun(_S) -> ?COMMA_INDENT(_S) end, false). +-dialyzer({nowarn_function, enc_list/4}). % Future compat enc_list([], _State, _SepEncoder, _NeedsSep) -> []; enc_list([{Elems, ElemEncoder} | Tail], State, SepEncoder, NeedsSep) -> diff --git a/lib/megaco/src/text/megaco_text_mini_parser.hrl b/lib/megaco/src/text/megaco_text_mini_parser.hrl index 487958af08..72d8168abf 100644 --- a/lib/megaco/src/text/megaco_text_mini_parser.hrl +++ b/lib/megaco/src/text/megaco_text_mini_parser.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -1167,6 +1167,7 @@ ensure_pathName({_TokenTag, _Line, Text}) -> % #'PropertyParm'{name = lists:reverse(Name), % value = [lists:reverse(Value)]}. +-dialyzer({nowarn_function, ensure_uint/3}). % Future compat ensure_uint({_TokenTag, Line, Val}, Min, Max) when is_integer(Val) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/src/text/megaco_text_parser_prev3a.hrl b/lib/megaco/src/text/megaco_text_parser_prev3a.hrl index edda850c3a..d7e847993a 100644 --- a/lib/megaco/src/text/megaco_text_parser_prev3a.hrl +++ b/lib/megaco/src/text/megaco_text_parser_prev3a.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -1589,6 +1589,7 @@ ensure_uint(Token, Min, Max) -> -ifdef(megaco_parser_inline). -compile({inline,[{ensure_uint,4}]}). -endif. +-dialyzer({nowarn_function, ensure_uint/4}). % Future compat ensure_uint(Val, Min, Max, Line) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/src/text/megaco_text_parser_prev3b.hrl b/lib/megaco/src/text/megaco_text_parser_prev3b.hrl index 4eaa3733c4..479f963c70 100644 --- a/lib/megaco/src/text/megaco_text_parser_prev3b.hrl +++ b/lib/megaco/src/text/megaco_text_parser_prev3b.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. 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. @@ -1635,6 +1635,7 @@ ensure_uint(Token, Min, Max) -> -ifdef(megaco_parser_inline). -compile({inline,[{ensure_uint,4}]}). -endif. +-dialyzer({nowarn_function, ensure_uint/4}). % Future compat ensure_uint(Val, Min, Max, Line) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/src/text/megaco_text_parser_prev3c.hrl b/lib/megaco/src/text/megaco_text_parser_prev3c.hrl index d2faad09d9..3ed9582308 100644 --- a/lib/megaco/src/text/megaco_text_parser_prev3c.hrl +++ b/lib/megaco/src/text/megaco_text_parser_prev3c.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. 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. @@ -1898,6 +1898,7 @@ ensure_uint(Token, Min, Max) -> -ifdef(megaco_parser_inline). -compile({inline,[{ensure_uint,4}]}). -endif. +-dialyzer({nowarn_function, ensure_uint/4}). % Future compat ensure_uint(Val, Min, Max, Line) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/src/text/megaco_text_parser_v1.hrl b/lib/megaco/src/text/megaco_text_parser_v1.hrl index f48ac745f0..3a19debba0 100644 --- a/lib/megaco/src/text/megaco_text_parser_v1.hrl +++ b/lib/megaco/src/text/megaco_text_parser_v1.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2000-2016. All Rights Reserved. +%% Copyright Ericsson AB 2000-2019. 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. @@ -1337,6 +1337,7 @@ ensure_uint(Token, Min, Max) -> -ifdef(megaco_parser_inline). -compile({inline,[{ensure_uint,4}]}). -endif. +-dialyzer({nowarn_function, ensure_uint/4}). % Future compat ensure_uint(Val, Min, Max, Line) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/src/text/megaco_text_parser_v2.hrl b/lib/megaco/src/text/megaco_text_parser_v2.hrl index f3c2f69193..270541d111 100644 --- a/lib/megaco/src/text/megaco_text_parser_v2.hrl +++ b/lib/megaco/src/text/megaco_text_parser_v2.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2019. 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. @@ -1563,6 +1563,7 @@ ensure_uint(Token, Min, Max) -> -ifdef(megaco_parser_inline). -compile({inline,[{ensure_uint,4}]}). -endif. +-dialyzer({nowarn_function, ensure_uint/4}). % Future compat ensure_uint(Val, Min, Max, Line) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/src/text/megaco_text_parser_v3.hrl b/lib/megaco/src/text/megaco_text_parser_v3.hrl index 38822e4952..7b8fff2080 100644 --- a/lib/megaco/src/text/megaco_text_parser_v3.hrl +++ b/lib/megaco/src/text/megaco_text_parser_v3.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2005-2016. All Rights Reserved. +%% Copyright Ericsson AB 2005-2019. 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. @@ -1920,6 +1920,7 @@ ensure_uint(Token, Min, Max) -> -ifdef(megaco_parser_inline). -compile({inline,[{ensure_uint,4}]}). -endif. +-dialyzer({nowarn_function, ensure_uint/4}). % Future compat ensure_uint(Val, Min, Max, Line) -> if is_integer(Min) andalso (Val >= Min) -> diff --git a/lib/megaco/test/megaco_examples_test.erl b/lib/megaco/test/megaco_examples_test.erl index 45a6c5011a..10ca0375f6 100644 --- a/lib/megaco/test/megaco_examples_test.erl +++ b/lib/megaco/test/megaco_examples_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2016. All Rights Reserved. +%% Copyright Ericsson AB 2001-2019. 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. @@ -31,6 +31,8 @@ -include_lib("megaco/include/megaco.hrl"). -include_lib("megaco/include/megaco_message_v1.hrl"). +-define(LIB, megaco_test_lib). + t() -> megaco_test_lib:t(?MODULE). t(Case) -> megaco_test_lib:t({?MODULE, Case}). @@ -56,9 +58,19 @@ load_examples() -> {error, Reason} -> {error, Reason}; Dir -> - [code:load_abs(filename:join([Dir, examples, simple, M])) || M <- example_modules()] + SimpleDir = filename:join([Dir, examples, simple]), + case code:add_path(SimpleDir) of + true -> + ok; + {error, What} -> + error_logger:error_msg("failed adding examples path: " + "~n ~p" + "~n", [What]), + {error, {failed_add_path, What}} + end end. + purge_examples() -> case code:lib_dir(megaco) of {error, Reason} -> @@ -67,6 +79,7 @@ purge_examples() -> [code:purge(M) || M <- example_modules()] end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Top test case @@ -87,94 +100,263 @@ end_per_group(_GroupName, Config) -> simple(suite) -> []; simple(Config) when is_list(Config) -> - ?ACQUIRE_NODES(1, Config), - d("simple -> proxy start",[]), - ProxyPid = megaco_test_lib:proxy_start({?MODULE, ?LINE}), + process_flag(trap_exit, true), + d("simple -> create node name(s)"), + [_Local, MGC, MG] = ?LIB:mk_nodes(3), %% Grrr + Nodes = [MGC, MG], + + d("simple -> start nodes"), + ok = ?LIB:start_nodes(Nodes, ?MODULE, ?LINE), + + MGCId = "MGC", + MGId = "MG", + + d("simple -> MGC proxy start (on ~p)", [MGC]), + MGCProxy = megaco_test_lib:proxy_start(MGC, "MGC"), + ?SLEEP(1000), + + d("simple -> MG proxy start (on ~p)", [MG]), + MGProxy = megaco_test_lib:proxy_start(MG, "MG"), + ?SLEEP(1000), + + MegacoStart = fun() -> megaco:start() end, + MegacoStartVerify = + fun(_, ok) -> ok; + (Id, Else) -> ?ERROR({failed_starting_megaco, Id, Else}) + end, - d("simple -> start megaco",[]), - ?VERIFY(ok, megaco:start()), + d("simple -> start MGC megaco"), + exec(MGCProxy, MGCId, MegacoStart, + fun(Res) -> MegacoStartVerify(MGCId, Res) end), + %% ?APPLY(MGCProxy, fun() -> ok = megaco:start() end), + ?SLEEP(1000), - d("simple -> start mgc",[]), - ?APPLY(ProxyPid, fun() -> megaco_simple_mgc:start() end), + d("simple -> start MG megaco"), + exec(MGProxy, MGId, MegacoStart, + fun(Res) -> MegacoStartVerify(MGId, Res) end), + %% ?APPLY(MGProxy, fun() -> ok = megaco:start() end), + ?SLEEP(1000), + + d("simple -> start mgc"), + start_mgc(MGCProxy), + ?SLEEP(1000), + + d("simple -> verify MGC info (no mg)"), + info(MGCProxy), + ?SLEEP(1000), + + d("simple -> verify MGC system_info(users) (no mg)"), + users(MGCProxy), + ?SLEEP(1000), + + d("simple -> start mg"), + start_mg(MGProxy), + ?SLEEP(1000), + + d("simple -> verify MGC info (mg)"), + info(MGCProxy), + ?SLEEP(1000), + + d("simple -> verify MGC system_info(users) (mg)"), + users(MGCProxy), + ?SLEEP(1000), + + d("simple -> verify MG info"), + info(MGProxy), + ?SLEEP(1000), + + d("simple -> verify MG system_info(users)"), + users(MGProxy), + ?SLEEP(1000), + + d("simple -> stop mgc"), + exec(MGCProxy, MGCId, + fun() -> megaco_simple_mgc:stop() end, + fun([_]) -> ok; + (L) when is_list(L) -> + ?ERROR({invalid_users, L}); + (X) -> + ?ERROR({invalid_result, X}) + end), + %% ?VERIFY(5, length()), + ?SLEEP(1000), + + d("simple -> verify MGC info (no mgc)"), + info(MGCProxy), + ?SLEEP(1000), + + d("simple -> verify MG info (no mgc)"), + info(MGProxy), + ?SLEEP(1000), + + d("simple -> verify MGC system_info(users) (no mgc)",[]), + users(MGCProxy), + ?SLEEP(1000), + + d("simple -> verify MG system_info(users) (no mgc)",[]), + users(MGProxy), + ?SLEEP(1000), + + MegacoStop = fun() -> megaco:stop() end, + MegacoStopVerify = + fun(_, ok) -> ok; + (Id, Else) -> ?ERROR({failed_stop_megaco, Id, Else}) + end, + + d("simple -> stop MG megaco",[]), + exec(MGProxy, MGId, MegacoStop, + fun(Res) -> MegacoStopVerify(MGId, Res) end), + %% ?VERIFY(ok, megaco:stop()), + ?SLEEP(1000), + + d("simple -> stop MGC megaco",[]), + exec(MGCProxy, MGCId, MegacoStop, + fun(Res) -> MegacoStopVerify(MGCId, Res) end), + %% ?VERIFY(ok, megaco:stop()), + ?SLEEP(1000), + + d("simple -> kill (exit) MG Proxy: ~p", [MGProxy]), + MGProxy ! {stop, self(), normal}, + receive + {'EXIT', MGProxy, _} -> + d("simple -> MG Proxy terminated"), + ok + end, + + d("simple -> kill (exit) MGC Proxy: ~p", [MGCProxy]), + MGCProxy ! {stop, self(), normal}, + receive + {'EXIT', MGCProxy, _} -> + d("simple -> MGC Proxy terminated"), + ok + end, + + d("simple -> stop ~p", [MGC]), + slave:stop(MGC), + + d("simple -> stop ~p", [MG]), + slave:stop(MG), + + d("simple -> done", []), + ok. + + +exec(Proxy, Id, Cmd, Verify) -> + ?APPLY(Proxy, Cmd), + receive + {res, Id, Res} -> + Verify(Res) + end. + + +start_mgc(Proxy) -> + ?APPLY(Proxy, + fun() -> + try megaco_simple_mgc:start() of + Res -> + Res + catch + C:E:S -> + {error, {{catched, C, E, S}}, code:get_path()} + end + end), receive {res, _, {ok, MgcAll}} when is_list(MgcAll) -> MgcBad = [MgcRes || MgcRes <- MgcAll, element(1, MgcRes) /= ok], ?VERIFY([], MgcBad), - %% MgcGood = MgcAll -- MgcBad, - %% MgcRecHandles = [MgcRH || {ok, _MgcPort, MgcRH} <- MgcGood], - - d("simple -> start mg",[]), - ?APPLY(ProxyPid, fun() -> megaco_simple_mg:start() end), - receive - {res, _, MgList} when is_list(MgList) andalso (length(MgList) =:= 4) -> - d("simple -> received res: ~p",[MgList]), - Verify = - fun({_MgMid, {TransId, Res}}) when TransId =:= 1 -> - case Res of - {ok, [AR]} when is_record(AR, 'ActionReply') -> - case AR#'ActionReply'.commandReply of - [{serviceChangeReply, SCR}] -> - case SCR#'ServiceChangeReply'.serviceChangeResult of - {serviceChangeResParms, MgcMid} when MgcMid /= asn1_NOVALUE -> - ok; - Error -> - ?ERROR(Error) - end; - Error -> - ?ERROR(Error) - end; - Error -> - ?ERROR(Error) - end; - (Error) -> - ?ERROR(Error) - end, - lists:map(Verify, MgList); - Error -> - ?ERROR(Error) - end; + ok; Error -> ?ERROR(Error) - end, - d("simple -> verify info()",[]), - info(), - d("simple -> verify system_info(users)",[]), - users(), - d("simple -> stop mgc",[]), - ?VERIFY(5, length(megaco_simple_mgc:stop())), - d("simple -> verify system_info(users)",[]), - users(), - d("simple -> stop megaco",[]), - ?VERIFY(ok, megaco:stop()), - d("simple -> kill (exit) ProxyPid: ~p",[ProxyPid]), - exit(ProxyPid, shutdown), % Controlled kill of transport supervisors + end. - ok. +start_mg(Proxy) -> + ?APPLY(Proxy, fun() -> + try megaco_simple_mg:start() of + Res -> + Res + catch + C:E:S -> + {error, {{catched, C, E, S}}, code:get_path()} + end + end), + receive + {res, _, MGs} when is_list(MGs) andalso (length(MGs) =:= 4) -> + verify_mgs(MGs); + Error -> + ?ERROR(Error) + end. -info() -> - case (catch megaco:info()) of - {'EXIT', _} = Error -> - ?ERROR(Error); - Info -> - ?LOG("Ok, ~p~n", [Info]) +verify_mgs(MGs) -> + Verify = + fun({_MgMid, {TransId, Res}}) when (TransId =:= 1) -> + case Res of + {ok, [AR]} when is_record(AR, 'ActionReply') -> + case AR#'ActionReply'.commandReply of + [{serviceChangeReply, SCR}] -> + case SCR#'ServiceChangeReply'.serviceChangeResult of + {serviceChangeResParms, MgcMid} + when (MgcMid =/= asn1_NOVALUE) -> + ok; + Error -> + ?ERROR(Error) + end; + Error -> + ?ERROR(Error) + end; + Error -> + ?ERROR(Error) + end; + (Error) -> + ?ERROR(Error) + end, + lists:map(Verify, MGs). + + +info(Proxy) -> + ?APPLY(Proxy, + fun() -> + try megaco:info() of + I -> I + catch + C:E:S -> + {error, {C, E, S}} + end + end), + receive + {res, _, Info} when is_list(Info) -> + ?LOG("Ok, ~p~n", [Info]); + {res, _, Error} -> + ?ERROR(Error) end. -users() -> - case (catch megaco:system_info(users)) of - {'EXIT', _} = Error -> - ?ERROR(Error); - Users -> - ?LOG("Ok, ~p~n", [Users]) + +users(Proxy) -> + ?APPLY(Proxy, + fun() -> + try megaco:system_info(users) of + I -> I + catch + C:E:S -> + {error, {C, E, S}} + end + end), + receive + {res, _, Info} when is_list(Info) -> + ?LOG("Ok, ~p~n", [Info]); + {res, _, Error} -> + ?ERROR(Error) end. -d(F,A) -> - d(get(dbg),F,A). +d(F) -> + d(F, []). +d(F, A) -> + d(get(dbg), F, A). -d(true,F,A) -> - io:format("DBG: " ++ F ++ "~n",A); +d(true, F, A) -> + io:format("DBG: ~s " ++ F ++ "~n", [?FTS() | A]); d(_, _F, _A) -> ok. diff --git a/lib/megaco/test/megaco_mess_test.erl b/lib/megaco/test/megaco_mess_test.erl index 7af6f26bf1..d3216c2a6d 100644 --- a/lib/megaco/test/megaco_mess_test.erl +++ b/lib/megaco/test/megaco_mess_test.erl @@ -597,6 +597,7 @@ request_and_no_reply(Config) when is_list(Config) -> ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], {ok, Mgc} = ?MGC_START(MgcNode, {deviceName, "ctrl"}, ET, [], ?MGC_VERBOSITY), + ?SLEEP(?SECONDS(1)), i("[MG] start"), Mg1Mid = {deviceName, "mg1"}, @@ -621,9 +622,13 @@ request_and_no_reply(Config) when is_list(Config) -> {pending_timer, PendingTmr}, {reply_timer, ReplyTmr}], {ok, Mg1} = ?MG_START(Mg1Node, Mg1Mid, text, tcp, MgConfig, ?MG_VERBOSITY), + ?SLEEP(?SECONDS(1)), {ok, Mg2} = ?MG_START(Mg2Node, Mg2Mid, text, udp, MgConfig, ?MG_VERBOSITY), + ?SLEEP(?SECONDS(1)), {ok, Mg3} = ?MG_START(Mg3Node, Mg3Mid, binary, tcp, MgConfig, ?MG_VERBOSITY), + ?SLEEP(?SECONDS(1)), {ok, Mg4} = ?MG_START(Mg4Node, Mg4Mid, binary, udp, MgConfig, ?MG_VERBOSITY), + ?SLEEP(?SECONDS(1)), d("MG1 user info: ~p", [?MG_USER_INFO(Mg1, all)]), d("MG1 conn info: ~p", [?MG_CONN_INFO(Mg1, all)]), @@ -639,55 +644,68 @@ request_and_no_reply(Config) when is_list(Config) -> d("service change result: ~p", [ServChRes1]), d("MG1 user info: ~p", [?MG_USER_INFO(Mg1, all)]), d("MG1 conn info: ~p", [?MG_CONN_INFO(Mg1, all)]), + ?SLEEP(?SECONDS(1)), i("[MG2] connect to the MGC (service change)"), ServChRes2 = ?MG_SERV_CHANGE(Mg2), d("service change result: ~p", [ServChRes2]), d("MG2 user info: ~p", [?MG_USER_INFO(Mg2, all)]), d("MG2 conn info: ~p", [?MG_CONN_INFO(Mg2, all)]), + ?SLEEP(?SECONDS(1)), i("[MG3] connect to the MGC (service change)"), ServChRes3 = ?MG_SERV_CHANGE(Mg3), d("service change result: ~p", [ServChRes3]), d("MG3 user info: ~p", [?MG_USER_INFO(Mg3, all)]), d("MG3 conn info: ~p", [?MG_CONN_INFO(Mg3, all)]), + ?SLEEP(?SECONDS(1)), i("[MG4] connect to the MGC (service change)"), ServChRes4 = ?MG_SERV_CHANGE(Mg4), d("service change result: ~p", [ServChRes4]), d("MG4 user info: ~p", [?MG_USER_INFO(Mg4, all)]), d("MG4 conn info: ~p", [?MG_CONN_INFO(Mg4, all)]), + ?SLEEP(?SECONDS(1)), d("tell the MGC to ignore requests"), ?MGC_REQ_PEND(Mgc, infinity), + ?SLEEP(?SECONDS(1)), d("[MG1] send the notify"), ?MG_NOTIF_REQ(Mg1), + ?SLEEP(?SECONDS(1)), d("[MG2] send the notify"), ?MG_NOTIF_REQ(Mg2), + ?SLEEP(?SECONDS(1)), d("[MG3] send the notify"), ?MG_NOTIF_REQ(Mg3), + ?SLEEP(?SECONDS(1)), d("[MG4] send the notify"), ?MG_NOTIF_REQ(Mg4), + ?SLEEP(?SECONDS(1)), d("[MG1] await notify reply"), {ok, {_Vsn1, {error, timeout}}} = ?MG_AWAIT_NOTIF_REP(Mg1), d("[MG1] received expected reply"), + ?SLEEP(?SECONDS(1)), d("[MG2] await notify reply"), {ok, {_Vsn2, {error, timeout}}} = ?MG_AWAIT_NOTIF_REP(Mg2), d("[MG2] received expected reply"), + ?SLEEP(?SECONDS(1)), d("[MG3] await notify reply"), {ok, {_Vsn3, {error, timeout}}} = ?MG_AWAIT_NOTIF_REP(Mg3), d("[MG3] received expected reply"), + ?SLEEP(?SECONDS(1)), d("[MG4] await notify reply"), {ok, {_Vsn4, {error, timeout}}} = ?MG_AWAIT_NOTIF_REP(Mg4), d("[MG4] received expected reply"), + ?SLEEP(?SECONDS(1)), d("MG1 user info: ~p", [?MG_USER_INFO(Mg1, all)]), d("MG1 conn info: ~p", [?MG_CONN_INFO(Mg1, all)]), @@ -13711,38 +13729,36 @@ i(F) -> i(F, []). i(F, A) -> - print(info, get(verbosity), now(), get(tc), "INF", F, A). + print(info, "INF", F, A). d(F) -> d(F, []). d(F, A) -> - print(debug, get(verbosity), now(), get(tc), "DBG", F, A). + print(debug, "DBG", F, A). -printable(_, debug) -> true; -printable(info, info) -> true; -printable(_,_) -> false. +print(Severity, PRE, FMT, ARGS) -> + print(Severity, get(verbosity), erlang:timestamp(), get(tc), PRE, FMT, ARGS). print(Severity, Verbosity, Ts, Tc, P, F, A) -> print(printable(Severity,Verbosity), Ts, Tc, P, F, A). -print(true, Ts, Tc, P, F, A) -> - io:format("*** [~s] ~s ~p ~s:~w ***" - "~n " ++ F ++ "~n", - [format_timestamp(Ts), P, self(), get(sname), Tc | A]); +print(true, TS, TC, P, F, A) -> + S = ?F("*** [~s] ~s ~p ~s:~w ***" + "~n " ++ F ++ "~n", + [megaco:format_timestamp(TS), P, self(), get(sname), TC | A]), + io:format("~s", [S]), + io:format(user, "~s", [S]); print(_, _, _, _, _, _) -> ok. -format_timestamp({_N1, _N2, N3} = Now) -> - {Date, Time} = calendar:now_to_datetime(Now), - {YYYY,MM,DD} = Date, - {Hour,Min,Sec} = Time, - FormatDate = - io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", - [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), - lists:flatten(FormatDate). + +printable(_, debug) -> true; +printable(info, info) -> true; +printable(_,_) -> false. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -13752,13 +13768,6 @@ to(To, Start) -> %% Time in milli seconds mtime() -> - {A,B,C} = erlang:now(), + {A,B,C} = erlang:timestamp(), A*1000000000+B*1000+(C div 1000). -%% random_init() -> -%% {A,B,C} = now(), -%% random:seed(A,B,C). - -%% random() -> -%% 10 * random:uniform(50). - diff --git a/lib/megaco/test/megaco_mib_test.erl b/lib/megaco/test/megaco_mib_test.erl index d644d6bc09..06ac7c08ed 100644 --- a/lib/megaco/test/megaco_mib_test.erl +++ b/lib/megaco/test/megaco_mib_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2002-2016. All Rights Reserved. +%% Copyright Ericsson AB 2002-2019. 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. @@ -24,7 +24,32 @@ %%---------------------------------------------------------------------- -module(megaco_mib_test). --compile(export_all). +-export([ + t/0, t/1, + + all/0, + groups/0, + init_per_testcase/2, + end_per_testcase/2, + init_per_group/2, + end_per_group/2, + + plain/1, + connect/1, + traffic/1, + + mg/3, + mgc/3, + + handle_connect/3, + handle_disconnect/4, + handle_syntax_error/4, + handle_message_error/4, + handle_trans_request/4, + handle_trans_long_request/4, + handle_trans_reply/5, + handle_trans_ack/5 + ]). -include("megaco_test_lib.hrl"). -include_lib("megaco/include/megaco.hrl"). @@ -54,6 +79,7 @@ t(Case) -> megaco_test_lib:t({?MODULE, Case}). %% Test server callbacks init_per_testcase(Case, Config) -> + progress("init_per_testcase -> ~w", [Case]), process_flag(trap_exit, true), case Case of traffic -> @@ -65,6 +91,7 @@ init_per_testcase(Case, Config) -> end. end_per_testcase(Case, Config) -> + progress("end_per_testcase -> ~w", [Case]), process_flag(trap_exit, false), megaco_test_lib:end_per_testcase(Case, Config). @@ -197,6 +224,7 @@ connect(Config) when is_list(Config) -> put(verbosity, ?TEST_VERBOSITY), put(sname, "TEST"), i("connect -> starting"), + progress("start nodes"), MgcNode = make_node_name(mgc), Mg1Node = make_node_name(mg1), Mg2Node = make_node_name(mg2), @@ -209,67 +237,85 @@ connect(Config) when is_list(Config) -> %% Start the MGC and MGs ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], + progress("start MGC"), {ok, Mgc} = start_mgc(MgcNode, {deviceName, "ctrl"}, ET, ?MGC_VERBOSITY), + progress("start MG1"), {ok, Mg1} = start_mg(Mg1Node, {deviceName, "mg1"}, text, tcp, ?MG_VERBOSITY), + progress("start MG2"), {ok, Mg2} = start_mg(Mg2Node, {deviceName, "mg2"}, binary, udp, ?MG_VERBOSITY), %% Collect the initial statistics (should be zero if anything) + progress("collect initial MG1 stats"), {ok, Mg1Stats0} = get_stats(Mg1, 1), d("connect -> stats for Mg1: ~n~p", [Mg1Stats0]), + progress("collect initial MG2 stats"), {ok, Mg2Stats0} = get_stats(Mg2, 1), d("connect -> stats for Mg2: ~n~p", [Mg2Stats0]), + progress("collect initial MGC stats"), {ok, MgcStats0} = get_stats(Mgc, 1), d("connect -> stats for Mgc: ~n~p", [MgcStats0]), %% Ask Mg1 to do a service change + progress("perform MG1 service change"), {ok, Res1} = service_change(Mg1), d("connect -> (Mg1) service change result: ~p", [Res1]), %% Collect the statistics + progress("collect MG1 statustics (after service change)"), {ok, Mg1Stats1} = get_stats(Mg1, 1), d("connect -> stats for Mg1: ~n~p", [Mg1Stats1]), + progress("collect MGC statistics (after MG1 service change)"), {ok, MgcStats1} = get_stats(Mgc, 1), d("connect -> stats (1) for Mgc: ~n~p", [MgcStats1]), {ok, MgcStats2} = get_stats(Mgc, 2), d("connect -> stats (2) for Mgc: ~n~p", [MgcStats2]), %% Ask Mg2 to do a service change + progress("perform MG2 service change"), {ok, Res2} = service_change(Mg2), d("connect -> (Mg2) service change result: ~p", [Res2]), %% Collect the statistics + progress("collect MG2 statustics (after service change)"), {ok, Mg2Stats1} = get_stats(Mg2, 1), d("connect -> stats for Mg1: ~n~p", [Mg2Stats1]), + progress("collect MGC statistics (after MG2 service change)"), {ok, MgcStats3} = get_stats(Mgc, 1), d("connect -> stats (1) for Mgc: ~n~p", [MgcStats3]), {ok, MgcStats4} = get_stats(Mgc, 2), d("connect -> stats (2) for Mgc: ~n~p", [MgcStats4]), %% Tell Mg1 to stop + progress("stop MG1"), stop(Mg1), %% Collect the statistics + progress("collect MGC statistics (after MG1 stop)"), {ok, MgcStats5} = get_stats(Mgc, 1), d("connect -> stats (1) for Mgc: ~n~p", [MgcStats5]), {ok, MgcStats6} = get_stats(Mgc, 2), d("connect -> stats (2) for Mgc: ~n~p", [MgcStats6]), %% Tell Mg2 to stop + progress("stop MG2"), stop(Mg2), %% Collect the statistics + progress("collect MGC statistics (after MG2 stop)"), {ok, MgcStats7} = get_stats(Mgc, 1), d("connect -> stats (1) for Mgc: ~n~p", [MgcStats7]), {ok, MgcStats8} = get_stats(Mgc, 2), d("connect -> stats (2) for Mgc: ~n~p", [MgcStats8]), %% Tell Mgc to stop + progress("stop MGC"), stop(Mgc), i("connect -> done", []), + progress("done"), ok. @@ -284,6 +330,7 @@ traffic(Config) when is_list(Config) -> put(verbosity, ?TEST_VERBOSITY), put(sname, "TEST"), i("traffic -> starting"), + progress("start nodes"), MgcNode = make_node_name(mgc), Mg1Node = make_node_name(mg1), Mg2Node = make_node_name(mg2), @@ -302,11 +349,13 @@ traffic(Config) when is_list(Config) -> %% Start the MGC and MGs i("traffic -> start the MGC"), + progress("start MGC"), ET = [{text,tcp}, {text,udp}, {binary,tcp}, {binary,udp}], {ok, Mgc} = start_mgc(MgcNode, {deviceName, "ctrl"}, ET, ?MGC_VERBOSITY), i("traffic -> start and connect the MGs"), + progress("start and connect MGs"), MgConf0 = [{Mg1Node, "mg1", text, tcp}, {Mg2Node, "mg2", text, udp}, {Mg3Node, "mg3", binary, tcp}, @@ -315,36 +364,42 @@ traffic(Config) when is_list(Config) -> %% Collect and check the MGs statistics i("traffic -> collect and check the MGs stats"), + progress("collect and verify MGs (initial) stats"), traffic_verify_mg_stats(MgConf, 1, 1), %% Collect and check the MGC statistics - i("traffic -> collect and check the MGC stats"), + i("traffic -> collect and check the MGC (initial) stats"), + progress("collect and verify MGC stats"), {ok, MgcStats1} = get_stats(Mgc, 1), d("traffic -> stats (1) for Mgc: ~n~p~n", [MgcStats1]), traffic_verify_mgc_stats(Mgc, 1, 1), - sleep(1000), + ?SLEEP(1000), %% And apply some load - i("traffic -> apply traffic load"), + i("traffic -> apply traffic load (1)"), + progress("apply some load (1)"), ok = traffic_apply_load(MgConf), %% Await completion of load part and the collect traffic - i("traffic -> await load competion"), + i("traffic -> await load (1) competion"), + progress("await load (1) completion"), ok = traffic_await_load_complete(MgConf), - sleep(1000), + ?SLEEP(1000), i("traffic -> collect and check the MGs statistics"), + progress("collect and verify MGs (after load 1) stats"), traffic_verify_mg_stats(MgConf, 1 + ?LOAD_COUNTER_START, 1 + ?LOAD_COUNTER_START), i("traffic -> collect and check the MGC statistics"), + progress("collect and verify MGC (after load 1) stats"), {ok, MgcStats3} = get_stats(Mgc, 1), d("traffic -> stats (1) for Mgc: ~n~p~n", [MgcStats3]), traffic_verify_mgc_stats(Mgc, @@ -352,60 +407,70 @@ traffic(Config) when is_list(Config) -> 1 + ?LOAD_COUNTER_START), - sleep(1000), + ?SLEEP(1000), %% Reset counters i("traffic -> reset the MGs statistics"), + progress("reset MGs stats"), traffic_reset_mg_stats(MgConf), i("traffic -> collect and check the MGs statistics"), + progress("collect and verify MGs (after reset) stats"), traffic_verify_mg_stats(MgConf, 0, 0), i("traffic -> reset the MGC statistics"), + progress("reset MGC stats"), traffic_reset_mgc_stats(Mgc), i("traffic -> collect and check the MGC statistics"), + progress("collect and verify MGC (after reset) stats"), traffic_verify_mgc_stats(Mgc, 0, 0), - sleep(1000), + ?SLEEP(1000), %% And apply some load - i("traffic -> apply traffic load"), + i("traffic -> apply traffic load (2)"), + progress("apply some load (2)"), ok = traffic_apply_load(MgConf), %% Await completion of load part and the collect traffic - i("traffic -> await load competion"), + i("traffic -> await load (2) competion"), + progress("await load (2) completion"), ok = traffic_await_load_complete(MgConf), - sleep(1000), + ?SLEEP(1000), i("traffic -> collect and check the MGs statistics"), + progress("collect and verify MGs (after load 2) stats"), traffic_verify_mg_stats(MgConf, ?LOAD_COUNTER_START, ?LOAD_COUNTER_START), i("traffic -> collect and check the MGC statistics"), + progress("collect and verify MGC (after load 2) stats"), traffic_verify_mgc_stats(Mgc, ?LOAD_COUNTER_START, ?LOAD_COUNTER_START), - sleep(1000), + ?SLEEP(1000), %% Tell MGs to stop i("traffic -> stop the MGs"), + progress("stop MGs"), traffic_stop_mg(MgConf), - sleep(1000), + ?SLEEP(1000), %% Collect the statistics i("traffic -> collect the MGC statistics"), + progress("collect and verify MGC (after MGs stop) stats"), {ok, MgcStats7} = get_stats(Mgc, 1), d("traffic -> stats (1) for Mgc: ~n~p~n", [MgcStats7]), {ok, MgcStats8} = get_stats(Mgc, 2), @@ -413,9 +478,11 @@ traffic(Config) when is_list(Config) -> %% Tell Mgc to stop i("traffic -> stop the MGC"), + progress("stop MGC"), stop(Mgc), i("traffic -> done", []), + progress("done"), ok. @@ -516,10 +583,15 @@ traffic_verify_get_stats(S, Stats) -> traffic_verify_counter(Name, Counter, Counters, Expected) -> case lists:keysearch(Counter, 1, Counters) of {value, {Counter, Expected}} -> + i("counter ~w verified for ~p", [Counter, Name]), ok; {value, {Counter, Val}} -> + i("counter ~w *not* verified for ~p: " + "~n Expected: ~w" + "~n Actual: ~w", [Counter, Name, Expected, Val]), exit({illegal_counter_value, Counter, Val, Expected, Name}); false -> + i("counter ~w *not* found for ~p", [Counter, Name]), exit({not_found, Counter, Counters, Name, Expected}) end. @@ -536,8 +608,7 @@ traffic_connect_mg(Node, Name, Coding, Trans) -> %% Ask the MGs to do a service change {ok, Res} = service_change(Pid), - d("traffic_connect_mg -> (~s) service change result: ~p", [Name,Res]), - + d("traffic_connect_mg -> (~s) service change result: ~p", [Name, Res]), Pid. @@ -549,7 +620,9 @@ traffic_get_mg_stats([], Acc) -> lists:reverse(Acc); traffic_get_mg_stats([{Name, Pid}|Mgs], Acc) -> {ok, Stats} = get_stats(Pid, 1), - d("traffic_get_mg_stats -> stats for ~s: ~n~p~n", [Name, Stats]), + d("traffic_get_mg_stats -> stats for ~s: " + "~n ~p" + "~n", [Name, Stats]), traffic_get_mg_stats(Mgs, [{Name, Stats}|Acc]). @@ -903,7 +976,7 @@ mgc_tcp_create_listen(Sup, Opts, MaxN, N, _InitialReason) ok -> Sup; {error, {could_not_start_listener, {gen_tcp_listen, eaddrinuse} = Reason}} -> - sleep(N * 200), + ?SLEEP(N * 200), mgc_tcp_create_listen(Sup, Opts, MaxN, N + 1, Reason); {error, Reason} -> throw({error, {failed_starting_tcp_listen, Reason}}); @@ -1044,7 +1117,6 @@ mg(Parent, Verbosity, Config) -> mg_init(Config) -> d("mg_init -> entry"), - random_init(), Mid = get_conf(local_mid, Config), RI = get_conf(receive_info, Config), d("mg_init -> start megaco"), @@ -1088,12 +1160,12 @@ mg_loop(#mg{state = State} = S) -> %% Give me statistics {statistics, 1, Parent} when S#mg.parent == Parent -> i("mg_loop(~p) -> got request for statistics 1", [State]), - {ok, Gen} = megaco:get_stats(), - CH = S#mg.conn_handle, - Reason = {statistics, CH}, - Pid = megaco:conn_info(CH, control_pid), - SendMod = megaco:conn_info(CH, send_mod), - SendHandle = megaco:conn_info(CH, send_handle), + {ok, Gen} = megaco:get_stats(), + CH = S#mg.conn_handle, + Reason = {statistics, CH}, + Pid = megaco:conn_info(CH, control_pid), + SendMod = megaco:conn_info(CH, send_mod), + SendHandle = megaco:conn_info(CH, send_handle), {ok, Trans} = case SendMod of megaco_tcp -> megaco_tcp:get_stats(SendHandle); @@ -1247,13 +1319,16 @@ mg_start_udp(MgcPort, RH) -> d("start udp transport"), case megaco_udp:start_transport() of {ok, Sup} -> - {ok, LocalHost} = inet:gethostname(), + %% Some linux (Ubuntu) has "crap" in their /etc/hosts, that + %% causes problem for us in this case (UDP). So we can't use + %% local host. Try instead to "figure out" tha actual address... + LocalAddr = which_local_addr(), Opts = [{port, 0}, {receive_handle, RH}], case megaco_udp:open(Sup, Opts) of {ok, Handle, ControlPid} -> MgcMid = preliminary_mid, SendHandle = megaco_udp:create_send_handle(Handle, - LocalHost, + LocalAddr, MgcPort), {ok, ConnHandle} = megaco:connect(RH, MgcMid, @@ -1528,10 +1603,6 @@ request(Pid, Request) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -sleep(X) -> - receive after X -> ok end. - - error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A). @@ -1583,6 +1654,44 @@ get_conf(Key, Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +which_local_addr() -> + case inet:getifaddrs() of + {ok, IFs} -> + which_local_addr(IFs); + {error, Reason} -> + i("Failed get local address: " + "~n ~p", [Reason]), + ?SKIP({failed_get_local_addr, Reason}) + end. + +which_local_addr([]) -> + ?SKIP(failed_get_local_addr); +which_local_addr([{"lo" = _IfName, _IfOpts}|IFs]) -> + which_local_addr(IFs); +which_local_addr([{"br-" ++ _ = _IfName, _IfOpts}|IFs]) -> + which_local_addr(IFs); +which_local_addr([{"docker" ++ _ = _IfName, _IfOpts}|IFs]) -> + which_local_addr(IFs); +which_local_addr([{_IfName, IfOpts}|IFs]) -> + case which_local_addr2(IfOpts) of + {ok, Addr} -> + Addr; + error -> + which_local_addr(IFs) + end. + + +which_local_addr2([]) -> + error; +which_local_addr2([{addr, Addr}|_]) + when (size(Addr) =:= 4) andalso (element(1, Addr) =/= 127) -> + {ok, Addr}; +which_local_addr2([_|T]) -> + which_local_addr2(T). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + i(F) -> i(F, []). @@ -1605,19 +1714,22 @@ print(Severity, Verbosity, P, F, A) -> print(printable(Severity,Verbosity), P, F, A). print(true, P, F, A) -> - io:format("~s~p:~s: " ++ F ++ "~n", [P, self(), get(sname) | A]); + io:format("~s~p:~s:~s: " ++ F ++ "~n", [P, self(), get(sname), ?FTS() | A]); print(_, _, _, _) -> ok. -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +progress(F) -> + progress(F, []). + +progress(F, A) -> + io:format(user, "~s " ++ F ++ "~n", [?FTS()|A]). -random_init() -> - {A,B,C} = now(), - random:seed(A,B,C). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% random() -> - 10 * random:uniform(50). + 10 * rand:uniform(50). apply_load_timer() -> erlang:send_after(random(), self(), apply_load_timeout). diff --git a/lib/megaco/test/megaco_test_lib.erl b/lib/megaco/test/megaco_test_lib.erl index 3934a3a957..0617b96456 100644 --- a/lib/megaco/test/megaco_test_lib.erl +++ b/lib/megaco/test/megaco_test_lib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. 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. @@ -26,7 +26,47 @@ -module(megaco_test_lib). --compile(export_all). +%% -compile(export_all). + +-export([ + log/4, + error/3, + + sleep/1, + hours/1, minutes/1, seconds/1, + formated_timestamp/0, format_timestamp/1, + + skip/3, + non_pc_tc_maybe_skip/4, + os_based_skip/1, + + flush/0, + still_alive/1, + watchdog/2, + + display_alloc_info/0, + display_system_info/1, display_system_info/2, display_system_info/3, + + tickets/1, + prepare_test_case/5, + + t/1, + groups/1, + init_suite/2, + end_suite/2, + init_group/3, + end_group/3, + t/2, + init_per_testcase/2, + end_per_testcase/2, + + proxy_start/1, proxy_start/2, + + mk_nodes/1, + start_nodes/3 + ]). + +-export([do_eval/4, proxy_init/2]). -include("megaco_test_lib.hrl"). @@ -53,6 +93,13 @@ minutes(N) -> trunc(N * 1000 * 60). seconds(N) -> trunc(N * 1000). +formated_timestamp() -> + format_timestamp(os:timestamp()). + +format_timestamp(TS) -> + megaco:format_timestamp(TS). + + %% ---------------------------------------------------------------- %% Conditional skip of testcases %% @@ -367,7 +414,7 @@ eval(Mod, Fun, Config) -> Flag = process_flag(trap_exit, true), put(megaco_test_server, true), Config2 = Mod:init_per_testcase(Fun, Config), - Pid = spawn_link(?MODULE, do_eval, [self(), Mod, Fun, Config2]), + Pid = spawn_link(fun() -> do_eval(self(), Mod, Fun, Config2) end), R = wait_for_evaluator(Pid, Mod, Fun, Config2, []), Mod:end_per_testcase(Fun, Config2), erase(megaco_test_server), @@ -719,6 +766,7 @@ still_alive(Pid) -> end end. + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% The proxy process @@ -730,32 +778,51 @@ proxy_start(Node, ProxyId) -> proxy_init(ProxyId, Controller) -> process_flag(trap_exit, true), - ?LOG("[~p] proxy started by ~p~n",[ProxyId, Controller]), + IdStr = proxyid2string(ProxyId), + put(id, IdStr), + ?LOG("[~s] proxy started by ~p~n", [IdStr, Controller]), proxy_loop(ProxyId, Controller). proxy_loop(OwnId, Controller) -> receive {'EXIT', Controller, Reason} -> - p("proxy_loop -> received exit from controller" + pprint("proxy_loop -> received exit from controller" + "~n Reason: ~p", [Reason]), + exit(Reason); + {stop, Controller, Reason} -> + p("proxy_loop -> received stop from controller" "~n Reason: ~p" "~n", [Reason]), exit(Reason); + {apply, Fun} -> - p("proxy_loop -> received apply request~n", []), + pprint("proxy_loop -> received apply request"), Res = Fun(), - p("proxy_loop -> apply result: " - "~n ~p" - "~n", [Res]), + pprint("proxy_loop -> apply result: " + "~n ~p", [Res]), Controller ! {res, OwnId, Res}, proxy_loop(OwnId, Controller); OtherMsg -> - p("proxy_loop -> received unknown message: " - "~n OtherMsg: ~p" - "~n", [OtherMsg]), + pprint("proxy_loop -> received unknown message: " + "~n ~p", [OtherMsg]), Controller ! {msg, OwnId, OtherMsg}, proxy_loop(OwnId, Controller) end. +proxyid2string(Id) when is_list(Id) -> + Id; +proxyid2string(Id) when is_atom(Id) -> + atom_to_list(Id); +proxyid2string(Id) -> + f("~p", [Id]). + +pprint(F) -> + pprint(F, []). + +pprint(F, A) -> + io:format("[~s] ~p ~s " ++ F ++ "~n", + [get(id), self(), formated_timestamp() | A]). + %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Test server callbacks @@ -837,7 +904,7 @@ reset_kill_timer(Config) -> end. watchdog(Pid, Time) -> - erlang:now(), + _ = os:timestamp(), receive stop -> ok @@ -897,7 +964,10 @@ default_config() -> [{nodes, default_nodes()}, {ts, megaco}]. default_nodes() -> - mk_nodes(2, []). + mk_nodes(3, []). + +mk_nodes(N) when (N > 0) -> + mk_nodes(N, []). mk_nodes(0, Nodes) -> Nodes; @@ -937,5 +1007,11 @@ start_nodes([Node | Nodes], File, Line) -> start_nodes([], _File, _Line) -> ok. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +f(F, A) -> + lists:flatten(io_lib:format(F, A)). + p(F, A) -> io:format("~p~w:" ++ F ++ "~n", [self(), ?MODULE |A]). diff --git a/lib/megaco/test/megaco_test_lib.hrl b/lib/megaco/test/megaco_test_lib.hrl index 79a1493c40..409f8d52e5 100644 --- a/lib/megaco/test/megaco_test_lib.hrl +++ b/lib/megaco/test/megaco_test_lib.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. 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. @@ -32,6 +32,8 @@ -define(ERROR(Reason), megaco_test_lib:error(Reason, ?MODULE, ?LINE)). +-define(F(FMT, ARGS), lists:flatten(io_lib:format(FMT, ARGS))). + -define(OS_BASED_SKIP(Skippable), megaco_test_lib:os_based_skip(Skippable)). @@ -80,9 +82,8 @@ -define(SLEEP(MSEC), megaco_test_lib:sleep(MSEC)). --define(M(), megaco_test_lib:millis()). --define(MDIFF(A,B), megaco_test_lib:millis_diff(A,B)). - -define(HOURS(T), megaco_test_lib:hours(T)). -define(MINUTES(T), megaco_test_lib:minutes(T)). -define(SECONDS(T), megaco_test_lib:seconds(T)). +-define(FTS(), megaco:format_timestamp(erlang:timestamp())). + diff --git a/lib/megaco/test/megaco_timer_test.erl b/lib/megaco/test/megaco_timer_test.erl index 34479f7838..d3e8e27636 100644 --- a/lib/megaco/test/megaco_timer_test.erl +++ b/lib/megaco/test/megaco_timer_test.erl @@ -332,7 +332,7 @@ integer_timer_start_and_expire(Config) when is_list(Config) -> receive {timeout, Timeout} -> ok - after Timeout + 100 -> + after Timeout + 500 -> tmr_stop(Ref), error(no_timeout) end, @@ -359,7 +359,12 @@ integer_timer_start_and_stop(Config) when is_list(Config) -> {timeout, Timeout} -> error(bad_timeout) after Timeout - 100 -> - tmr_stop(Ref) + case tmr_stop(Ref) of + ok -> + ok; + CancelRes -> + ?SKIP({cancel_failed, CancelRes}) + end end, %% Make sure it does not reach us after we attempted to stop it. @@ -438,21 +443,6 @@ print1(_, _, _, _) -> print(Prefix, F, A) -> io:format("*** [~s] ~s ~p ~s:~w ***" "~n " ++ F ++ "~n", - [formated_timestamp(), Prefix, self(), get(sname), get(tc) | A]). + [?FTS(), Prefix, self(), get(sname), get(tc) | A]). - - - -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -formated_timestamp() -> - format_timestamp(now()). - -format_timestamp({_N1, _N2, N3} = Now) -> - {Date, Time} = calendar:now_to_datetime(Now), - {YYYY,MM,DD} = Date, - {Hour,Min,Sec} = Time, - FormatDate = - io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", - [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), - lists:flatten(FormatDate). diff --git a/lib/megaco/test/megaco_trans_test.erl b/lib/megaco/test/megaco_trans_test.erl index 9786307860..fb44a3c6e6 100644 --- a/lib/megaco/test/megaco_trans_test.erl +++ b/lib/megaco/test/megaco_trans_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2016. All Rights Reserved. +%% Copyright Ericsson AB 2003-2019. 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. @@ -27,7 +27,46 @@ %%---------------------------------------------------------------------- -module(megaco_trans_test). --compile(export_all). +%% -compile(export_all). +-export([ + all/0, + groups/0, + init_per_group/2, end_per_group/2, + init_per_testcase/2, end_per_testcase/2, + + single_ack/1, + multi_ack_timeout/1, + multi_ack_maxcount/1, + + single_trans_req/1, + multi_trans_req_timeout/1, + multi_trans_req_maxcount1/1, + multi_trans_req_maxcount2/1, + multi_trans_req_maxsize1/1, + multi_trans_req_maxsize2/1, + + single_trans_req_and_ack/1, + multi_trans_req_and_ack_timeout/1, + multi_trans_req_and_ack_ackmaxcount/1, + multi_trans_req_and_ack_reqmaxcount/1, + multi_trans_req_and_ack_maxsize1/1, + multi_trans_req_and_ack_maxsize2/1, + + single_trans_req_and_pending/1, + multi_trans_req_and_pending/1, + multi_trans_req_and_ack_and_pending/1, + multi_ack_and_pending/1, + + multi_trans_req_and_reply/1, + multi_trans_req_and_ack_and_reply/1, + multi_ack_and_reply/1, + + otp_7192_1/1, + otp_7192_2/1, + otp_7192_3/1, + + t/0, t/1 + ]). -include("megaco_test_lib.hrl"). -include_lib("megaco/include/megaco.hrl"). @@ -44,44 +83,42 @@ -define(A5555, ["11111111", "11111111", "00000000"]). -define(A5556, ["11111111", "11111111", "11111111"]). --define(MGC_START(Pid, Mid, ET, Verb), - megaco_test_mgc:start(Pid, Mid, ET, Verb)). --define(MGC_STOP(Pid), megaco_test_mgc:stop(Pid)). --define(MGC_GET_STATS(Pid, No), megaco_test_mgc:get_stats(Pid, No)). --define(MGC_RESET_STATS(Pid), megaco_test_mgc:reset_stats(Pid)). --define(MGC_REQ_DISC(Pid,To), megaco_test_mgc:request_discard(Pid,To)). --define(MGC_REQ_PEND(Pid,To), megaco_test_mgc:request_pending(Pid,To)). --define(MGC_REQ_HAND(Pid), megaco_test_mgc:request_handle(Pid)). --define(MGC_REQ_HANDS(Pid), megaco_test_mgc:request_handle_sloppy(Pid)). --define(MGC_UPDATE_UI(Pid,Tag,Val), - megaco_test_mgc:update_user_info(Pid,Tag,Val)). --define(MGC_UPDATE_CI(Pid,Tag,Val), - megaco_test_mgc:update_conn_info(Pid,Tag,Val)). --define(MGC_USER_INFO(Pid,Tag), megaco_test_mgc:user_info(Pid,Tag)). --define(MGC_CONN_INFO(Pid,Tag), megaco_test_mgc:conn_info(Pid,Tag)). --define(MGC_ACK_INFO(Pid,To), megaco_test_mgc:ack_info(Pid,To)). --define(MGC_REQ_INFO(Pid,To), megaco_test_mgc:req_info(Pid,To)). +-define(MG, megaco_test_mg). +-define(MGC, megaco_test_mgc). + +-define(MGC_START(Pid, Mid, ET, Verb), ?MGC:start(Pid, Mid, ET, Verb)). +-define(MGC_STOP(Pid), ?MGC:stop(Pid)). +-define(MGC_GET_STATS(Pid, No), ?MGC:get_stats(Pid, No)). +-define(MGC_RESET_STATS(Pid), ?MGC:reset_stats(Pid)). +-define(MGC_REQ_DISC(Pid,To), ?MGC:request_discard(Pid,To)). +-define(MGC_REQ_PEND(Pid,To), ?MGC:request_pending(Pid,To)). +-define(MGC_REQ_HAND(Pid), ?MGC:request_handle(Pid)). +-define(MGC_REQ_HANDS(Pid), ?MGC:request_handle_sloppy(Pid)). +-define(MGC_UPDATE_UI(Pid,Tag,Val), ?MGC:update_user_info(Pid,Tag,Val)). +-define(MGC_UPDATE_CI(Pid,Tag,Val), ?MGC:update_conn_info(Pid,Tag,Val)). +-define(MGC_USER_INFO(Pid,Tag), ?MGC:user_info(Pid,Tag)). +-define(MGC_CONN_INFO(Pid,Tag), ?MGC:conn_info(Pid,Tag)). +-define(MGC_ACK_INFO(Pid,To), ?MGC:ack_info(Pid,To)). +-define(MGC_REQ_INFO(Pid,To), ?MGC:req_info(Pid,To)). -define(MG_START(Pid, Mid, Enc, Transp, Conf, Verb), - megaco_test_mg:start(Pid, Mid, Enc, Transp, Conf, Verb)). --define(MG_STOP(Pid), megaco_test_mg:stop(Pid)). --define(MG_GET_STATS(Pid), megaco_test_mg:get_stats(Pid)). --define(MG_RESET_STATS(Pid), megaco_test_mg:reset_stats(Pid)). --define(MG_SERV_CHANGE(Pid), megaco_test_mg:service_change(Pid)). --define(MG_NOTIF_RAR(Pid), megaco_test_mg:notify_request_and_reply(Pid)). --define(MG_NOTIF_REQ(Pid), megaco_test_mg:notify_request(Pid)). --define(MG_NOTIF_AR(Pid), megaco_test_mg:await_notify_reply(Pid)). --define(MG_CANCEL(Pid,R), megaco_test_mg:cancel_request(Pid,R)). --define(MG_APPLY_LOAD(Pid,CntStart), megaco_test_mg:apply_load(Pid,CntStart)). --define(MG_UPDATE_UI(Pid,Tag,Val), - megaco_test_mg:update_user_info(Pid,Tag,Val)). --define(MG_UPDATE_CI(Pid,Tag,Val), - megaco_test_mg:update_conn_info(Pid,Tag,Val)). --define(MG_USER_INFO(Pid,Tag), megaco_test_mg:user_info(Pid,Tag)). --define(MG_CONN_INFO(Pid,Tag), megaco_test_mg:conn_info(Pid,Tag)). --define(MG_GRP_REQ(Pid,N), megaco_test_mg:group_requests(Pid,N)). --define(MG_ACK_INFO(Pid,To), megaco_test_mg:ack_info(Pid,To)). --define(MG_REP_INFO(Pid,To), megaco_test_mg:rep_info(Pid,To)). + ?MG:start(Pid, Mid, Enc, Transp, Conf, Verb)). +-define(MG_STOP(Pid), ?MG:stop(Pid)). +-define(MG_GET_STATS(Pid), ?MG:get_stats(Pid)). +-define(MG_RESET_STATS(Pid), ?MG:reset_stats(Pid)). +-define(MG_SERV_CHANGE(Pid), ?MG:service_change(Pid)). +-define(MG_NOTIF_RAR(Pid), ?MG:notify_request_and_reply(Pid)). +-define(MG_NOTIF_REQ(Pid), ?MG:notify_request(Pid)). +-define(MG_NOTIF_AR(Pid), ?MG:await_notify_reply(Pid)). +-define(MG_CANCEL(Pid,R), ?MG:cancel_request(Pid,R)). +-define(MG_APPLY_LOAD(Pid,CntStart), ?MG:apply_load(Pid,CntStart)). +-define(MG_UPDATE_UI(Pid,Tag,Val), ?MG:update_user_info(Pid,Tag,Val)). +-define(MG_UPDATE_CI(Pid,Tag,Val), ?MG:update_conn_info(Pid,Tag,Val)). +-define(MG_USER_INFO(Pid,Tag), ?MG:user_info(Pid,Tag)). +-define(MG_CONN_INFO(Pid,Tag), ?MG:conn_info(Pid,Tag)). +-define(MG_GRP_REQ(Pid,N), ?MG:group_requests(Pid,N)). +-define(MG_ACK_INFO(Pid,To), ?MG:ack_info(Pid,To)). +-define(MG_REP_INFO(Pid,To), ?MG:rep_info(Pid,To)). t() -> megaco_test_lib:t(?MODULE). t(Case) -> megaco_test_lib:t({?MODULE, Case}). @@ -104,35 +141,77 @@ end_per_testcase(Case, Config) -> %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% all() -> - [{group, ack}, {group, trans_req}, - {group, trans_req_and_ack}, {group, pending}, - {group, reply}, {group, tickets}]. + [{group, ack}, + {group, trans_req}, + {group, trans_req_and_ack}, + {group, pending}, + {group, reply}, + {group, tickets}]. groups() -> - [{ack, [], - [single_ack, multi_ack_timeout, multi_ack_maxcount]}, - {trans_req, [], - [single_trans_req, multi_trans_req_timeout, - multi_trans_req_maxcount1, multi_trans_req_maxcount2, - multi_trans_req_maxsize1, multi_trans_req_maxsize2]}, - {trans_req_and_ack, [], - [single_trans_req_and_ack, - multi_trans_req_and_ack_timeout, - multi_trans_req_and_ack_ackmaxcount, - multi_trans_req_and_ack_reqmaxcount, - multi_trans_req_and_ack_maxsize1, - multi_trans_req_and_ack_maxsize2]}, - {pending, [], - [single_trans_req_and_pending, - multi_trans_req_and_pending, - multi_trans_req_and_ack_and_pending, - multi_ack_and_pending]}, - {reply, [], - [multi_trans_req_and_reply, - multi_trans_req_and_ack_and_reply, - multi_ack_and_reply]}, - {tickets, [], [{group, otp_7192}]}, - {otp_7192, [], [otp_7192_1, otp_7192_2, otp_7192_3]}]. + [ + {ack, [], ack_cases()}, + {trans_req, [], trans_req_cases()}, + {trans_req_and_ack, [], trans_req_and_ack_cases()}, + {pending, [], pending_cases()}, + {reply, [], reply_cases()}, + {tickets, [], tickets_cases()}, + {otp_7192, [], otp_7192_cases()} + ]. + +ack_cases() -> + [ + single_ack, + multi_ack_timeout, + multi_ack_maxcount + ]. + +trans_req_cases() -> + [ + single_trans_req, + multi_trans_req_timeout, + multi_trans_req_maxcount1, + multi_trans_req_maxcount2, + multi_trans_req_maxsize1, + multi_trans_req_maxsize2 + ]. + +trans_req_and_ack_cases() -> + [ + single_trans_req_and_ack, + multi_trans_req_and_ack_timeout, + multi_trans_req_and_ack_ackmaxcount, + multi_trans_req_and_ack_reqmaxcount, + multi_trans_req_and_ack_maxsize1, + multi_trans_req_and_ack_maxsize2 + ]. + +pending_cases() -> + [ + single_trans_req_and_pending, + multi_trans_req_and_pending, + multi_trans_req_and_ack_and_pending, + multi_ack_and_pending + ]. + +reply_cases() -> + [ + multi_trans_req_and_reply, + multi_trans_req_and_ack_and_reply, + multi_ack_and_reply + ]. + +tickets_cases() -> + [ + {group, otp_7192} + ]. + +otp_7192_cases() -> + [ + otp_7192_1, + otp_7192_2, + otp_7192_3 + ]. init_per_group(_GroupName, Config) -> Config. @@ -156,8 +235,8 @@ single_ack(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -231,8 +310,8 @@ multi_ack_timeout(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -308,8 +387,8 @@ multi_ack_maxcount(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -393,8 +472,8 @@ single_trans_req(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -628,26 +707,26 @@ str_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -str_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = str_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% str_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = str_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). str_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -str_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = str_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% str_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = str_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -778,12 +857,12 @@ str_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -str_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = str_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% str_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = str_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). str_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -794,12 +873,12 @@ str_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -str_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = str_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% str_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = str_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -827,8 +906,8 @@ multi_trans_req_timeout(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -1063,26 +1142,26 @@ mtrt_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrt_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrt_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrt_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrt_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrt_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrt_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrt_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrt_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrt_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -1224,12 +1303,12 @@ mtrt_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrt_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrt_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrt_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrt_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrt_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -1240,12 +1319,12 @@ mtrt_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrt_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrt_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrt_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrt_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -1273,8 +1352,8 @@ multi_trans_req_maxcount1(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -1509,26 +1588,26 @@ mtrmc1_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrmc1_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrmc1_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc1_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrmc1_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrmc1_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrmc1_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrmc1_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc1_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrmc1_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -1675,12 +1754,12 @@ mtrmc1_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrmc1_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrmc1_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc1_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrmc1_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrmc1_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -1691,12 +1770,12 @@ mtrmc1_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrmc1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrmc1_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrmc1_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -1725,8 +1804,8 @@ multi_trans_req_maxcount2(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -1978,13 +2057,13 @@ mtrmc2_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrmc2_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrmc2_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc2_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrmc2_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrmc2_mgc_notify_reply_ar1(Cid, Tid) -> NR = cre_notifyReply([Tid]), @@ -1995,13 +2074,13 @@ mtrmc2_mgc_notify_reply_ar2(Cid, Tids) -> CRs = [cre_cmdReply(cre_notifyReply([Tid])) || Tid <- Tids], cre_actionReply(Cid, CRs). -mtrmc2_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrmc2_mgc_notify_reply_ar1(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc2_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrmc2_mgc_notify_reply_ar1(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -2163,12 +2242,12 @@ mtrmc2_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrmc2_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrmc2_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc2_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrmc2_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrmc2_mg_notify_request_ar1(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -2194,12 +2273,12 @@ mtrmc2_mg_notify_request_ar2(Rid, Tid, Cid) -> CRs = [F(N) || N <- Ns], cre_actionReq(Cid, CRs). -mtrmc2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrmc2_mg_notify_request_ar1(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrmc2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrmc2_mg_notify_request_ar1(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -2229,8 +2308,8 @@ multi_trans_req_maxsize1(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -2466,26 +2545,26 @@ mtrms1_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrms1_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrms1_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms1_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrms1_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrms1_mgc_notify_reply_ar1(Cid, Tid) -> NR = cre_notifyReply([Tid]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrms1_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrms1_mgc_notify_reply_ar1(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms1_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrms1_mgc_notify_reply_ar1(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -2633,12 +2712,12 @@ mtrms1_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrms1_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrms1_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms1_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrms1_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrms1_mg_notify_request_ar1(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -2649,18 +2728,16 @@ mtrms1_mg_notify_request_ar1(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrms1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrms1_mg_notify_request_ar1(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrms1_mg_notify_request_ar1(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrms1_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). @@ -2681,8 +2758,8 @@ multi_trans_req_maxsize2(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -2934,13 +3011,13 @@ mtrms2_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrms2_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrms2_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms2_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrms2_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrms2_mgc_notify_reply_ar1(Cid, Tid) -> NR = cre_notifyReply([Tid]), @@ -2951,13 +3028,13 @@ mtrms2_mgc_notify_reply_ar2(Cid, Tids) -> CRs = [cre_cmdReply(cre_notifyReply([Tid])) || Tid <- Tids], cre_actionReply(Cid, CRs). -mtrms2_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrms2_mgc_notify_reply_ar1(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms2_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrms2_mgc_notify_reply_ar1(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -3115,12 +3192,12 @@ mtrms2_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrms2_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrms2_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms2_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrms2_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrms2_mg_notify_request_ar1(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -3146,18 +3223,16 @@ mtrms2_mg_notify_request_ar2(Rid, Tid, Cid) -> CRs = [F(N) || N <- Ns], cre_actionReq(Cid, CRs). -mtrms2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrms2_mg_notify_request_ar1(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrms2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrms2_mg_notify_request_ar1(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrms2_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -3175,8 +3250,8 @@ single_trans_req_and_ack(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -3439,26 +3514,26 @@ straa_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -straa_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = straa_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% straa_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = straa_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). straa_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -straa_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = straa_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% straa_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = straa_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -3605,12 +3680,12 @@ straa_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -straa_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = straa_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% straa_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = straa_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). straa_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -3621,12 +3696,12 @@ straa_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -straa_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = straa_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% straa_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = straa_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -3634,9 +3709,7 @@ straa_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% straa_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). @@ -3656,8 +3729,8 @@ multi_trans_req_and_ack_timeout(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -3926,26 +3999,26 @@ mtrtaat_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrtaat_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrtaat_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaat_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrtaat_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaat_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrtaat_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrtaat_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaat_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrtaat_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -4098,12 +4171,12 @@ mtrtaat_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaat_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrtaat_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaat_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrtaat_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaat_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -4114,12 +4187,12 @@ mtrtaat_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaat_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrtaat_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaat_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrtaat_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -4127,9 +4200,7 @@ mtrtaat_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% mtrtaat_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -4147,8 +4218,8 @@ multi_trans_req_and_ack_ackmaxcount(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -4422,26 +4493,26 @@ mtrtaaamc_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrtaaamc_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrtaaamc_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaaamc_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrtaaamc_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaaamc_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrtaaamc_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrtaaamc_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaaamc_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrtaaamc_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -4596,12 +4667,12 @@ mtrtaaamc_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaaamc_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrtaaamc_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaaamc_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrtaaamc_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaaamc_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -4612,12 +4683,12 @@ mtrtaaamc_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaaamc_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrtaaamc_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaaamc_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrtaaamc_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -4625,9 +4696,7 @@ mtrtaaamc_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% mtrtaaamc_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -4645,8 +4714,8 @@ multi_trans_req_and_ack_reqmaxcount(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -4919,26 +4988,26 @@ mtrtaarac_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrtaarac_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrtaarac_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaarac_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrtaarac_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaarac_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrtaarac_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrtaarac_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaarac_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrtaarac_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -5093,12 +5162,12 @@ mtrtaarac_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaarac_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrtaarac_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaarac_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrtaarac_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaarac_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -5109,12 +5178,12 @@ mtrtaarac_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaarac_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrtaarac_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaarac_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrtaarac_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -5142,8 +5211,8 @@ multi_trans_req_and_ack_maxsize1(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -5416,26 +5485,26 @@ mtrtaams1_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrtaams1_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrtaams1_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams1_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrtaams1_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaams1_mgc_notify_reply_ar(Cid, TermId) -> NR = cre_notifyReply([TermId]), CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtrtaams1_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrtaams1_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams1_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrtaams1_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -5589,12 +5658,12 @@ mtrtaams1_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaams1_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrtaams1_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams1_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrtaams1_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaams1_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -5605,12 +5674,12 @@ mtrtaams1_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaams1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrtaams1_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrtaams1_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -5618,9 +5687,7 @@ mtrtaams1_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% mtrtaams1_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -5638,8 +5705,8 @@ multi_trans_req_and_ack_maxsize2(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -5915,13 +5982,13 @@ mtrtaams2_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtrtaams2_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtrtaams2_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams2_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtrtaams2_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaams2_mgc_notify_reply_ar1(Cid, TermId) -> NR = cre_notifyReply([TermId]), @@ -5932,13 +5999,13 @@ mtrtaams2_mgc_notify_reply_ar2(Cid, Tids) -> CRs = [cre_cmdReply(cre_notifyReply([Tid])) || Tid <- Tids], cre_actionReply(Cid, CRs). -mtrtaams2_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtrtaams2_mgc_notify_reply_ar1(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams2_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtrtaams2_mgc_notify_reply_ar1(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -6093,12 +6160,12 @@ mtrtaams2_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtrtaams2_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtrtaams2_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams2_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtrtaams2_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtrtaams2_mg_notify_request_ar1(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -6124,12 +6191,12 @@ mtrtaams2_mg_notify_request_ar2(Rid, Tid, Cid) -> CRs = [F(N) || N <- Ns], cre_actionReq(Cid, CRs). -mtrtaams2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtrtaams2_mg_notify_request_ar1(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtrtaams2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtrtaams2_mg_notify_request_ar1(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -6137,9 +6204,7 @@ mtrtaams2_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% mtrtaams2_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -6177,8 +6242,8 @@ multi_trans_req_and_ack_and_pending(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -6469,13 +6534,13 @@ mtraaap_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtraaap_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtraaap_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaap_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtraaap_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtraaap_mgc_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "44000000"), @@ -6491,13 +6556,13 @@ mtraaap_mgc_notify_reply_ar(Cid, TermId) -> CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtraaap_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtraaap_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaap_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtraaap_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -6639,38 +6704,38 @@ mtraaap_mg_verify_service_change_reply(Else) -> "~n Else: ~p~n", [Else]), {error, Else, ok}. -mtraaap_mg_verify_notify_request_fun() -> - fun(Ev) -> - mtraaap_mg_verify_notify_request(Ev) - end. - -mtraaap_mg_verify_notify_request( - {handle_trans_request, _, ?VERSION, [AR]}) -> - io:format("mtraaap_mg_verify_notify_request -> ok" - "~n AR: ~p~n", [AR]), - case AR of - #'ActionRequest'{contextId = 1 = Cid, - commandRequests = [CR]} -> - #'CommandRequest'{command = Cmd} = CR, - {notifyReq, NR} = Cmd, - #'NotifyRequest'{terminationID = [Tid], - observedEventsDescriptor = OED, - errorDescriptor = asn1_NOVALUE} = NR, - #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, - #'ObservedEvent'{eventName = "al/of"} = OE, - Reply = {discard_ack, [mtraaap_mg_notify_reply_ar(Cid, Tid)]}, - {ok, 3000, AR, Reply}; - _ -> - ED = mtraaap_err_desc(AR), - ErrReply = {discard_ack, ED}, - {error, AR, ErrReply} - end; -mtraaap_mg_verify_notify_request(Else) -> - io:format("mtraaap_mg_verify_notify_request:fun -> unknown" - "~n Else: ~p~n", [Else]), - ED = mtraaap_err_desc(Else), - ErrReply = {discard_ack, ED}, - {error, Else, ErrReply}. +%% mtraaap_mg_verify_notify_request_fun() -> +%% fun(Ev) -> +%% mtraaap_mg_verify_notify_request(Ev) +%% end. + +%% mtraaap_mg_verify_notify_request( +%% {handle_trans_request, _, ?VERSION, [AR]}) -> +%% io:format("mtraaap_mg_verify_notify_request -> ok" +%% "~n AR: ~p~n", [AR]), +%% case AR of +%% #'ActionRequest'{contextId = 1 = Cid, +%% commandRequests = [CR]} -> +%% #'CommandRequest'{command = Cmd} = CR, +%% {notifyReq, NR} = Cmd, +%% #'NotifyRequest'{terminationID = [Tid], +%% observedEventsDescriptor = OED, +%% errorDescriptor = asn1_NOVALUE} = NR, +%% #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, +%% #'ObservedEvent'{eventName = "al/of"} = OE, +%% Reply = {discard_ack, [mtraaap_mg_notify_reply_ar(Cid, Tid)]}, +%% {ok, 3000, AR, Reply}; +%% _ -> +%% ED = mtraaap_err_desc(AR), +%% ErrReply = {discard_ack, ED}, +%% {error, AR, ErrReply} +%% end; +%% mtraaap_mg_verify_notify_request(Else) -> +%% io:format("mtraaap_mg_verify_notify_request:fun -> unknown" +%% "~n Else: ~p~n", [Else]), +%% ED = mtraaap_err_desc(Else), +%% ErrReply = {discard_ack, ED}, +%% {error, Else, ErrReply}. mtraaap_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> @@ -6691,17 +6756,17 @@ mtraaap_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtraaap_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtraaap_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaap_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtraaap_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). -mtraaap_mg_notify_reply_ar(Cid, TermId) -> - NR = cre_notifyReply([TermId]), - CR = cre_cmdReply(NR), - cre_actionReply(Cid, [CR]). +%% mtraaap_mg_notify_reply_ar(Cid, TermId) -> +%% NR = cre_notifyReply([TermId]), +%% CR = cre_cmdReply(NR), +%% cre_actionReply(Cid, [CR]). mtraaap_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -6712,12 +6777,12 @@ mtraaap_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtraaap_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtraaap_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaap_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtraaap_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -6725,9 +6790,7 @@ mtraaap_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% mtraaap_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -6765,8 +6828,8 @@ multi_trans_req_and_ack_and_reply(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), ok = megaco_test_lib:start_nodes([MgcNode, MgNode], ?FILE, ?LINE), @@ -7061,13 +7124,13 @@ mtraaar_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -mtraaar_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = mtraaar_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaar_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = mtraaar_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). mtraaar_mgc_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "44000000"), @@ -7083,13 +7146,13 @@ mtraaar_mgc_notify_reply_ar(Cid, TermId) -> CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -mtraaar_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = mtraaar_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaar_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = mtraaar_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -7232,38 +7295,38 @@ mtraaar_mg_verify_service_change_reply(Else) -> "~n Else: ~p~n", [Else]), {error, Else, ok}. -mtraaar_mg_verify_notify_request_fun() -> - fun(Ev) -> - mtraaar_mg_verify_notify_request(Ev) - end. - -mtraaar_mg_verify_notify_request( - {handle_trans_request, _, ?VERSION, [AR]}) -> - io:format("mtraaar_mg_verify_notify_request -> ok" - "~n AR: ~p~n", [AR]), - case AR of - #'ActionRequest'{contextId = 1 = Cid, - commandRequests = [CR]} -> - #'CommandRequest'{command = Cmd} = CR, - {notifyReq, NR} = Cmd, - #'NotifyRequest'{terminationID = [Tid], - observedEventsDescriptor = OED, - errorDescriptor = asn1_NOVALUE} = NR, - #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, - #'ObservedEvent'{eventName = "al/of"} = OE, - Reply = {discard_ack, [mtraaar_mg_notify_reply_ar(Cid, Tid)]}, - {ok, AR, Reply}; - _ -> - ED = mtraaar_err_desc(AR), - ErrReply = {discard_ack, ED}, - {error, AR, ErrReply} - end; -mtraaar_mg_verify_notify_request(Else) -> - io:format("mtraaar_mg_verify_notify_request -> unknown" - "~n Else: ~p~n", [Else]), - ED = mtraaar_err_desc(Else), - ErrReply = {discard_ack, ED}, - {error, Else, ErrReply}. +%% mtraaar_mg_verify_notify_request_fun() -> +%% fun(Ev) -> +%% mtraaar_mg_verify_notify_request(Ev) +%% end. + +%% mtraaar_mg_verify_notify_request( +%% {handle_trans_request, _, ?VERSION, [AR]}) -> +%% io:format("mtraaar_mg_verify_notify_request -> ok" +%% "~n AR: ~p~n", [AR]), +%% case AR of +%% #'ActionRequest'{contextId = 1 = Cid, +%% commandRequests = [CR]} -> +%% #'CommandRequest'{command = Cmd} = CR, +%% {notifyReq, NR} = Cmd, +%% #'NotifyRequest'{terminationID = [Tid], +%% observedEventsDescriptor = OED, +%% errorDescriptor = asn1_NOVALUE} = NR, +%% #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, +%% #'ObservedEvent'{eventName = "al/of"} = OE, +%% Reply = {discard_ack, [mtraaar_mg_notify_reply_ar(Cid, Tid)]}, +%% {ok, AR, Reply}; +%% _ -> +%% ED = mtraaar_err_desc(AR), +%% ErrReply = {discard_ack, ED}, +%% {error, AR, ErrReply} +%% end; +%% mtraaar_mg_verify_notify_request(Else) -> +%% io:format("mtraaar_mg_verify_notify_request -> unknown" +%% "~n Else: ~p~n", [Else]), +%% ED = mtraaar_err_desc(Else), +%% ErrReply = {discard_ack, ED}, +%% {error, Else, ErrReply}. mtraaar_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> @@ -7284,17 +7347,17 @@ mtraaar_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtraaar_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = mtraaar_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaar_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = mtraaar_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). -mtraaar_mg_notify_reply_ar(Cid, TermId) -> - NR = cre_notifyReply([TermId]), - CR = cre_cmdReply(NR), - cre_actionReply(Cid, [CR]). +%% mtraaar_mg_notify_reply_ar(Cid, TermId) -> +%% NR = cre_notifyReply([TermId]), +%% CR = cre_cmdReply(NR), +%% cre_actionReply(Cid, [CR]). mtraaar_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -7305,12 +7368,12 @@ mtraaar_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -mtraaar_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = mtraaar_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% mtraaar_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = mtraaar_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -7318,9 +7381,7 @@ mtraaar_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% mtraaar_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -7348,8 +7409,8 @@ otp_7192_1(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), MgMid = {deviceName,"mg"}, @@ -7643,13 +7704,13 @@ otp71921_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -otp71921_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = otp71921_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71921_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = otp71921_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). otp71921_mgc_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "44000000"), @@ -7665,13 +7726,13 @@ otp71921_mgc_notify_reply_ar(Cid, TermId) -> CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -otp71921_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = otp71921_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71921_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = otp71921_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -7813,38 +7874,38 @@ otp71921_mg_verify_service_change_reply(Else) -> "~n Else: ~p~n", [Else]), {error, Else, ok}. -otp71921_mg_verify_notify_request_fun() -> - fun(Ev) -> - otp71921_mg_verify_notify_request(Ev) - end. - -otp71921_mg_verify_notify_request( - {handle_trans_request, _, ?VERSION, [AR]}) -> - io:format("otp71921_mg_verify_notify_request -> ok" - "~n AR: ~p~n", [AR]), - case AR of - #'ActionRequest'{contextId = 1 = Cid, - commandRequests = [CR]} -> - #'CommandRequest'{command = Cmd} = CR, - {notifyReq, NR} = Cmd, - #'NotifyRequest'{terminationID = [Tid], - observedEventsDescriptor = OED, - errorDescriptor = asn1_NOVALUE} = NR, - #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, - #'ObservedEvent'{eventName = "al/of"} = OE, - Reply = {discard_ack, [otp71921_mg_notify_reply_ar(Cid, Tid)]}, - {ok, AR, Reply}; - _ -> - ED = otp71921_err_desc(AR), - ErrReply = {discard_ack, ED}, - {error, AR, ErrReply} - end; -otp71921_mg_verify_notify_request(Else) -> - io:format("otp71921_mg_verify_notify_request -> unknown" - "~n Else: ~p~n", [Else]), - ED = otp71921_err_desc(Else), - ErrReply = {discard_ack, ED}, - {error, Else, ErrReply}. +%% otp71921_mg_verify_notify_request_fun() -> +%% fun(Ev) -> +%% otp71921_mg_verify_notify_request(Ev) +%% end. + +%% otp71921_mg_verify_notify_request( +%% {handle_trans_request, _, ?VERSION, [AR]}) -> +%% io:format("otp71921_mg_verify_notify_request -> ok" +%% "~n AR: ~p~n", [AR]), +%% case AR of +%% #'ActionRequest'{contextId = 1 = Cid, +%% commandRequests = [CR]} -> +%% #'CommandRequest'{command = Cmd} = CR, +%% {notifyReq, NR} = Cmd, +%% #'NotifyRequest'{terminationID = [Tid], +%% observedEventsDescriptor = OED, +%% errorDescriptor = asn1_NOVALUE} = NR, +%% #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, +%% #'ObservedEvent'{eventName = "al/of"} = OE, +%% Reply = {discard_ack, [otp71921_mg_notify_reply_ar(Cid, Tid)]}, +%% {ok, AR, Reply}; +%% _ -> +%% ED = otp71921_err_desc(AR), +%% ErrReply = {discard_ack, ED}, +%% {error, AR, ErrReply} +%% end; +%% otp71921_mg_verify_notify_request(Else) -> +%% io:format("otp71921_mg_verify_notify_request -> unknown" +%% "~n Else: ~p~n", [Else]), +%% ED = otp71921_err_desc(Else), +%% ErrReply = {discard_ack, ED}, +%% {error, Else, ErrReply}. otp71921_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> @@ -7865,17 +7926,17 @@ otp71921_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -otp71921_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = otp71921_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71921_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = otp71921_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). -otp71921_mg_notify_reply_ar(Cid, TermId) -> - NR = cre_notifyReply([TermId]), - CR = cre_cmdReply(NR), - cre_actionReply(Cid, [CR]). +%% otp71921_mg_notify_reply_ar(Cid, TermId) -> +%% NR = cre_notifyReply([TermId]), +%% CR = cre_cmdReply(NR), +%% cre_actionReply(Cid, [CR]). otp71921_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -7886,12 +7947,12 @@ otp71921_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -otp71921_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = otp71921_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71921_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = otp71921_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -7919,8 +7980,8 @@ otp_7192_2(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), MgMid = {deviceName,"mg"}, @@ -8213,13 +8274,13 @@ otp71922_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -otp71922_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = otp71922_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71922_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = otp71922_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). otp71922_mgc_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "44000000"), @@ -8235,13 +8296,13 @@ otp71922_mgc_notify_reply_ar(Cid, TermId) -> CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -otp71922_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = otp71922_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71922_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = otp71922_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -8379,38 +8440,38 @@ otp71922_mg_verify_service_change_reply(Else) -> "~n Else: ~p~n", [Else]), {error, Else, ok}. -otp71922_mg_verify_notify_request_fun() -> - fun(Ev) -> - otp71922_mg_verify_notify_request(Ev) - end. - -otp71922_mg_verify_notify_request( - {handle_trans_request, _, ?VERSION, [AR]}) -> - io:format("otp71922_mg_verify_notify_request -> ok" - "~n AR: ~p~n", [AR]), - case AR of - #'ActionRequest'{contextId = 1 = Cid, - commandRequests = [CR]} -> - #'CommandRequest'{command = Cmd} = CR, - {notifyReq, NR} = Cmd, - #'NotifyRequest'{terminationID = [Tid], - observedEventsDescriptor = OED, - errorDescriptor = asn1_NOVALUE} = NR, - #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, - #'ObservedEvent'{eventName = "al/of"} = OE, - Reply = {discard_ack, [otp71922_mg_notify_reply_ar(Cid, Tid)]}, - {ok, AR, Reply}; - _ -> - ED = otp71922_err_desc(AR), - ErrReply = {discard_ack, ED}, - {error, AR, ErrReply} - end; -otp71922_mg_verify_notify_request(Else) -> - io:format("otp71922_mg_verify_notify_request -> unknown" - "~n Else: ~p~n", [Else]), - ED = otp71922_err_desc(Else), - ErrReply = {discard_ack, ED}, - {error, Else, ErrReply}. +%% otp71922_mg_verify_notify_request_fun() -> +%% fun(Ev) -> +%% otp71922_mg_verify_notify_request(Ev) +%% end. + +%% otp71922_mg_verify_notify_request( +%% {handle_trans_request, _, ?VERSION, [AR]}) -> +%% io:format("otp71922_mg_verify_notify_request -> ok" +%% "~n AR: ~p~n", [AR]), +%% case AR of +%% #'ActionRequest'{contextId = 1 = Cid, +%% commandRequests = [CR]} -> +%% #'CommandRequest'{command = Cmd} = CR, +%% {notifyReq, NR} = Cmd, +%% #'NotifyRequest'{terminationID = [Tid], +%% observedEventsDescriptor = OED, +%% errorDescriptor = asn1_NOVALUE} = NR, +%% #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, +%% #'ObservedEvent'{eventName = "al/of"} = OE, +%% Reply = {discard_ack, [otp71922_mg_notify_reply_ar(Cid, Tid)]}, +%% {ok, AR, Reply}; +%% _ -> +%% ED = otp71922_err_desc(AR), +%% ErrReply = {discard_ack, ED}, +%% {error, AR, ErrReply} +%% end; +%% otp71922_mg_verify_notify_request(Else) -> +%% io:format("otp71922_mg_verify_notify_request -> unknown" +%% "~n Else: ~p~n", [Else]), +%% ED = otp71922_err_desc(Else), +%% ErrReply = {discard_ack, ED}, +%% {error, Else, ErrReply}. otp71922_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> @@ -8431,17 +8492,17 @@ otp71922_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -otp71922_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = otp71922_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71922_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = otp71922_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). -otp71922_mg_notify_reply_ar(Cid, TermId) -> - NR = cre_notifyReply([TermId]), - CR = cre_cmdReply(NR), - cre_actionReply(Cid, [CR]). +%% otp71922_mg_notify_reply_ar(Cid, TermId) -> +%% NR = cre_notifyReply([TermId]), +%% CR = cre_cmdReply(NR), +%% cre_actionReply(Cid, [CR]). otp71922_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -8452,12 +8513,12 @@ otp71922_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -otp71922_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = otp71922_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp71922_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = otp71922_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -8465,9 +8526,7 @@ otp71922_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% otp71922_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -8485,8 +8544,8 @@ otp_7192_3(Config) when is_list(Config) -> MgcNode = make_node_name(mgc), MgNode = make_node_name(mg), d("start nodes: " - "~n MgcNode: ~p" - "~n MgNode: ~p", + "~n MGC Node: ~p" + "~n MG Node: ~p", [MgcNode, MgNode]), MgMid = {deviceName,"mg"}, @@ -8779,13 +8838,13 @@ otp72923_mgc_service_change_reply_ar(Mid, Cid) -> CR = cre_cmdReply(SCR), cre_actionReply(Cid, [CR]). -otp72923_mgc_service_change_reply_msg(Mid, TransId, Cid) -> - AR = otp72923_mgc_service_change_reply_ar(Mid, Cid), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp72923_mgc_service_change_reply_msg(Mid, TransId, Cid) -> +%% AR = otp72923_mgc_service_change_reply_ar(Mid, Cid), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). otp72923_mgc_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "44000000"), @@ -8801,13 +8860,13 @@ otp72923_mgc_notify_reply_ar(Cid, TermId) -> CR = cre_cmdReply(NR), cre_actionReply(Cid, [CR]). -otp72923_mgc_notify_reply(Mid, TransId, Cid, TermId) -> - AR = otp72923_mgc_notify_reply_ar(Cid, TermId), - TRes = cre_transResult([AR]), - TR = cre_transReply(TransId, TRes), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp72923_mgc_notify_reply(Mid, TransId, Cid, TermId) -> +%% AR = otp72923_mgc_notify_reply_ar(Cid, TermId), +%% TRes = cre_transResult([AR]), +%% TR = cre_transReply(TransId, TRes), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -8946,38 +9005,38 @@ otp72923_mg_verify_service_change_reply(Else) -> "~n Else: ~p~n", [Else]), {error, Else, ok}. -otp72923_mg_verify_notify_request_fun() -> - fun(Ev) -> - otp72923_mg_verify_notify_request(Ev) - end. - -otp72923_mg_verify_notify_request( - {handle_trans_request, _, ?VERSION, [AR]}) -> - io:format("otp72923_mg_verify_notify_request -> ok" - "~n AR: ~p~n", [AR]), - case AR of - #'ActionRequest'{contextId = 1 = Cid, - commandRequests = [CR]} -> - #'CommandRequest'{command = Cmd} = CR, - {notifyReq, NR} = Cmd, - #'NotifyRequest'{terminationID = [Tid], - observedEventsDescriptor = OED, - errorDescriptor = asn1_NOVALUE} = NR, - #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, - #'ObservedEvent'{eventName = "al/of"} = OE, - Reply = {discard_ack, [otp72923_mg_notify_reply_ar(Cid, Tid)]}, - {ok, AR, Reply}; - _ -> - ED = otp72923_err_desc(AR), - ErrReply = {discard_ack, ED}, - {error, AR, ErrReply} - end; -otp72923_mg_verify_notify_request(Else) -> - io:format("otp72923_mg_verify_notify_request -> unknown" - "~n Else: ~p~n", [Else]), - ED = otp72923_err_desc(Else), - ErrReply = {discard_ack, ED}, - {error, Else, ErrReply}. +%% otp72923_mg_verify_notify_request_fun() -> +%% fun(Ev) -> +%% otp72923_mg_verify_notify_request(Ev) +%% end. + +%% otp72923_mg_verify_notify_request( +%% {handle_trans_request, _, ?VERSION, [AR]}) -> +%% io:format("otp72923_mg_verify_notify_request -> ok" +%% "~n AR: ~p~n", [AR]), +%% case AR of +%% #'ActionRequest'{contextId = 1 = Cid, +%% commandRequests = [CR]} -> +%% #'CommandRequest'{command = Cmd} = CR, +%% {notifyReq, NR} = Cmd, +%% #'NotifyRequest'{terminationID = [Tid], +%% observedEventsDescriptor = OED, +%% errorDescriptor = asn1_NOVALUE} = NR, +%% #'ObservedEventsDescriptor'{observedEventLst = [OE]} = OED, +%% #'ObservedEvent'{eventName = "al/of"} = OE, +%% Reply = {discard_ack, [otp72923_mg_notify_reply_ar(Cid, Tid)]}, +%% {ok, AR, Reply}; +%% _ -> +%% ED = otp72923_err_desc(AR), +%% ErrReply = {discard_ack, ED}, +%% {error, AR, ErrReply} +%% end; +%% otp72923_mg_verify_notify_request(Else) -> +%% io:format("otp72923_mg_verify_notify_request -> unknown" +%% "~n Else: ~p~n", [Else]), +%% ED = otp72923_err_desc(Else), +%% ErrReply = {discard_ack, ED}, +%% {error, Else, ErrReply}. otp72923_mg_verify_notify_reply({handle_trans_reply, _CH, ?VERSION, {ok, [AR]}, _}) -> @@ -8998,17 +9057,17 @@ otp72923_mg_service_change_request_ar(_Mid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -otp72923_mg_service_change_request_msg(Mid, TransId, Cid) -> - AR = otp72923_mg_service_change_request_ar(Mid, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp72923_mg_service_change_request_msg(Mid, TransId, Cid) -> +%% AR = otp72923_mg_service_change_request_ar(Mid, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). -otp72923_mg_notify_reply_ar(Cid, TermId) -> - NR = cre_notifyReply([TermId]), - CR = cre_cmdReply(NR), - cre_actionReply(Cid, [CR]). +%% otp72923_mg_notify_reply_ar(Cid, TermId) -> +%% NR = cre_notifyReply([TermId]), +%% CR = cre_cmdReply(NR), +%% cre_actionReply(Cid, [CR]). otp72923_mg_notify_request_ar(Rid, Tid, Cid) -> TT = cre_timeNotation("19990729", "22000000"), @@ -9019,12 +9078,12 @@ otp72923_mg_notify_request_ar(Rid, Tid, Cid) -> CR = cre_cmdReq(CMD), cre_actionReq(Cid, [CR]). -otp72923_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> - AR = otp72923_mg_notify_request_ar(Rid, TermId, Cid), - TR = cre_transReq(TransId, [AR]), - Trans = cre_transaction(TR), - Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), - cre_megacoMessage(Mess). +%% otp72923_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> +%% AR = otp72923_mg_notify_request_ar(Rid, TermId, Cid), +%% TR = cre_transReq(TransId, [AR]), +%% Trans = cre_transaction(TR), +%% Mess = cre_message(?VERSION, Mid, cre_transactions([Trans])), +%% cre_megacoMessage(Mess). %% @@ -9032,9 +9091,7 @@ otp72923_notify_request_msg(Mid, TransId, Rid, TermId, Cid) -> %% otp72923_err_desc(T) -> - EC = ?megaco_internal_gateway_error, - ET = lists:flatten(io_lib:format("~w",[T])), - #'ErrorDescriptor'{errorCode = EC, errorText = ET}. + cre_ErrDesc(T). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -9065,10 +9122,10 @@ cre_timeNotation(D,T) -> cre_obsEvent(Name, Not) -> #'ObservedEvent'{eventName = Name, timeNotation = Not}. -cre_obsEvent(Name, Not, Par) -> - #'ObservedEvent'{eventName = Name, - timeNotation = Not, - eventParList = Par}. +%% cre_obsEvent(Name, Not, Par) -> +%% #'ObservedEvent'{eventName = Name, +%% timeNotation = Not, +%% eventParList = Par}. cre_obsEvsDesc(Id, EvList) -> #'ObservedEventsDescriptor'{requestId = Id, @@ -9090,9 +9147,9 @@ cre_actionReq(CtxId, CmdReqs) when is_list(CmdReqs) -> #'ActionRequest'{contextId = CtxId, commandRequests = CmdReqs}. -cre_transReq(TransId, ARs) when is_list(ARs) -> - #'TransactionRequest'{transactionId = TransId, - actions = ARs}. +%% cre_transReq(TransId, ARs) when is_list(ARs) -> +%% #'TransactionRequest'{transactionId = TransId, +%% actions = ARs}. %% -- @@ -9120,14 +9177,14 @@ cre_actionReply(CtxId, CmdRep) -> #'ActionReply'{contextId = CtxId, commandReply = CmdRep}. -cre_transResult(ED) when is_record(ED, 'ErrorDescriptor') -> - {transactionError, ED}; -cre_transResult([AR|_] = ARs) when is_record(AR, 'ActionReply') -> - {actionReplies, ARs}. +%% cre_transResult(ED) when is_record(ED, 'ErrorDescriptor') -> +%% {transactionError, ED}; +%% cre_transResult([AR|_] = ARs) when is_record(AR, 'ActionReply') -> +%% {actionReplies, ARs}. -cre_transReply(TransId, Res) -> - #'TransactionReply'{transactionId = TransId, - transactionResult = Res}. +%% cre_transReply(TransId, Res) -> +%% #'TransactionReply'{transactionId = TransId, +%% transactionResult = Res}. %% -- @@ -9136,48 +9193,48 @@ cre_serviceChangeProf(Name, Ver) when is_list(Name) andalso is_integer(Ver) -> #'ServiceChangeProfile'{profileName = Name, version = Ver}. -cre_transaction(Trans) when is_record(Trans, 'TransactionRequest') -> - {transactionRequest, Trans}; -cre_transaction(Trans) when is_record(Trans, 'TransactionPending') -> - {transactionPending, Trans}; -cre_transaction(Trans) when is_record(Trans, 'TransactionReply') -> - {transactionReply, Trans}; -cre_transaction(Trans) when is_record(Trans, 'TransactionAck') -> - {transactionResponseAck, Trans}. - -cre_transactions(Trans) when is_list(Trans) -> - {transactions, Trans}. - -cre_message(Version, Mid, Body) -> - #'Message'{version = Version, - mId = Mid, - messageBody = Body}. - -cre_megacoMessage(Mess) -> - #'MegacoMessage'{mess = Mess}. +%% cre_transaction(Trans) when is_record(Trans, 'TransactionRequest') -> +%% {transactionRequest, Trans}; +%% cre_transaction(Trans) when is_record(Trans, 'TransactionPending') -> +%% {transactionPending, Trans}; +%% cre_transaction(Trans) when is_record(Trans, 'TransactionReply') -> +%% {transactionReply, Trans}; +%% cre_transaction(Trans) when is_record(Trans, 'TransactionAck') -> +%% {transactionResponseAck, Trans}. + +%% cre_transactions(Trans) when is_list(Trans) -> +%% {transactions, Trans}. + +%% cre_message(Version, Mid, Body) -> +%% #'Message'{version = Version, +%% mId = Mid, +%% messageBody = Body}. + +%% cre_megacoMessage(Mess) -> +%% #'MegacoMessage'{mess = Mess}. %% %% Common functions %% -encode_msg_fun(Mod, Conf) -> - fun(M) -> - Mod:encode_message(Conf, M) - end. -encode_msg_fun(Mod, Conf, Ver) -> - fun(M) -> - Mod:encode_message(Conf, Ver, M) - end. - -decode_msg_fun(Mod, Conf) -> - fun(M) -> - Mod:decode_message(Conf, M) - end. -decode_msg_fun(Mod, Conf, Ver) -> - fun(M) -> - Mod:decode_message(Conf, Ver, M) - end. +%% encode_msg_fun(Mod, Conf) -> +%% fun(M) -> +%% Mod:encode_message(Conf, M) +%% end. +%% encode_msg_fun(Mod, Conf, Ver) -> +%% fun(M) -> +%% Mod:encode_message(Conf, Ver, M) +%% end. + +%% decode_msg_fun(Mod, Conf) -> +%% fun(M) -> +%% Mod:decode_message(Conf, M) +%% end. +%% decode_msg_fun(Mod, Conf, Ver) -> +%% fun(M) -> +%% Mod:decode_message(Conf, Ver, M) +%% end. %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% @@ -9193,7 +9250,7 @@ await_ack(User, N, Timeout, Expected) when (N > 0) andalso is_integer(Timeout) - d("await_ack -> received another ack"), await_ack(User, N-1, Timeout - (tim() - T), Expected); {ack_received, User, UnExpected} -> - d("await_ack -> unexpected ack result: ~p", [UnExpected]), + e("await_ack -> unexpected ack result: ~p", [UnExpected]), exit({unexpected_ack_result, UnExpected, Expected}) after Timeout -> exit({await_ack_timeout, N}) @@ -9205,72 +9262,42 @@ await_ack(User, N, infinity, Expected) when N > 0 -> d("await_ack -> received another ack"), await_ack(User, N-1, infinity, Expected); {ack_received, User, UnExpected} -> - d("await_ack -> unexpected ack result: ~p", [UnExpected]), + e("await_ack -> unexpected ack result: ~p", [UnExpected]), exit({unexpected_ack_result, UnExpected, Expected}) end. -await_req(_User, 0, Timeout) -> - d("await_req -> done when Timeout = ~p", [Timeout]), - ok; -await_req(User, N, Timeout) when (N > 0) andalso is_integer(Timeout) -> - d("await_req -> entry with N: ~p, Timeout: ~p", [N,Timeout]), - T = tim(), - receive - {req_received, User, ARs} -> - d("await_req -> received req(s) when N = ~w", [N]), - N1 = await_req1(N, ARs), - await_req(User, N1, Timeout - (tim() - T)) - after Timeout -> - exit({await_req_timeout, N}) - end; -await_req(User, N, infinity) when N > 0 -> - d("await_req -> entry with N: ~p", [N]), - receive - {req_received, User, ARs} -> - d("await_req -> received req(s) when N = ~2",[N]), - N1 = await_req1(N, ARs), - await_req(User, N1, infinity) - end. - -await_req1(N, []) when N >= 0 -> - N; -await_req1(N, [AR|ARs]) when (N > 0) andalso is_record(AR, 'ActionRequest') -> - await_req1(N-1, ARs); -await_req1(N, ARs) -> - exit({unexpected_req_result, N, ARs}). - -% await_rep(_User, 0, Timeout) -> -% d("await_rep -> done when Timeout = ~p", [Timeout]), -% ok; -% await_rep(User, N, Timeout) when N > 0, integer(Timeout) -> -% d("await_rep -> entry with N: ~p, Timeout: ~p", [N,Timeout]), -% T = tim(), -% receive -% {rep_received, User, ARs} -> -% d("await_rep -> received rep(s)"), -% N1 = await_rep1(N, ARs), -% await_rep(User, N1, Timeout - (tim() - T)) -% after Timeout -> -% exit({await_rep_timeout, N}) -% end; -% await_rep(User, N, infinity) when N > 0 -> -% d("await_rep -> entry with N: ~p", [N]), -% receive -% {rep_received, User, ARs} -> -% d("await_rep -> received rep(s)"), -% N1 = await_rep1(N, ARs), -% await_rep(User, N1, infinity) -% end. - -% await_rep1(N, []) when N >= 0 -> -% N; -% await_rep1(N, [AR|ARs]) when N > 0, record(AR, 'ActionReply') -> -% await_rep1(N-1, ARs); -% await_rep1(N, ARs) -> -% exit({unexpected_rep_result, N, ARs}). +%% await_req(_User, 0, Timeout) -> +%% d("await_req -> done when Timeout = ~p", [Timeout]), +%% ok; +%% await_req(User, N, Timeout) when (N > 0) andalso is_integer(Timeout) -> +%% d("await_req -> entry with N: ~p, Timeout: ~p", [N,Timeout]), +%% T = tim(), +%% receive +%% {req_received, User, ARs} -> +%% d("await_req -> received req(s) when N = ~w", [N]), +%% N1 = await_req1(N, ARs), +%% await_req(User, N1, Timeout - (tim() - T)) +%% after Timeout -> +%% exit({await_req_timeout, N}) +%% end; +%% await_req(User, N, infinity) when N > 0 -> +%% d("await_req -> entry with N: ~p", [N]), +%% receive +%% {req_received, User, ARs} -> +%% d("await_req -> received req(s) when N = ~2",[N]), +%% N1 = await_req1(N, ARs), +%% await_req(User, N1, infinity) +%% end. + +%% await_req1(N, []) when N >= 0 -> +%% N; +%% await_req1(N, [AR|ARs]) when (N > 0) andalso is_record(AR, 'ActionRequest') -> +%% await_req1(N-1, ARs); +%% await_req1(N, ARs) -> +%% exit({unexpected_req_result, N, ARs}). tim() -> - {A,B,C} = erlang:now(), + {A,B,C} = erlang:timestamp(), A*1000000000+B*1000+(C div 1000). @@ -9291,7 +9318,8 @@ await_completion(Ids) -> d("OK => Reply: ~n~p", [Reply]), ok; {error, Reply} -> - d("ERROR => Reply: ~n~p", [Reply]), + e("await completion failed: " + "~n ~p", [Reply]), ?ERROR({failed, Reply}) end. @@ -9301,7 +9329,9 @@ await_completion(Ids, Timeout) -> d("OK => Reply: ~n~p", [Reply]), ok; {error, Reply} -> - d("ERROR => Reply: ~n~p", [Reply]), + e("await completion failed: " + "~n ~p" + "~n ~p", [Timeout, Reply]), ?ERROR({failed, Reply}) end. @@ -9310,64 +9340,71 @@ await_completion(Ids, Timeout) -> sleep(X) -> receive after X -> ok end. -error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A). +%% error_msg(F,A) -> error_logger:error_msg(F ++ "~n",A). %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% e(F) -> +%% e(F, []). + +e(F, A) -> + print(error, "ERR", F, A). + + i(F) -> i(F, []). i(F, A) -> - print(info, get(verbosity), now(), get(tc), "INF", F, A). + print(info, "INF", F, A). d(F) -> d(F, []). d(F, A) -> - print(debug, get(verbosity), now(), get(tc), "DBG", F, A). + print(debug, "DBG", F, A). -printable(_, debug) -> true; -printable(info, info) -> true; -printable(_,_) -> false. +print(Severity, P, F, A) -> + print2(printable(Severity), P, F, A). -print(Severity, Verbosity, Ts, Tc, P, F, A) -> - print(printable(Severity,Verbosity), Ts, Tc, P, F, A). +printable(Sev) -> + printable(Sev, get(verbosity)). -print(true, Ts, Tc, P, F, A) -> - io:format("*** [~s] ~s ~p ~s:~w ***" - "~n " ++ F ++ "~n", - [format_timestamp(Ts), P, self(), get(tc), Tc | A]); -print(_, _, _, _, _, _) -> +printable(_, debug) -> true; +printable(info, info) -> true; +printable(error, _) -> true; +printable(_,_) -> false. + + +print2(true, P, F, A) -> + TS = erlang:timestamp(), + TC = get(tc), + S = ?F("*** [~s] ~s ~p ~w ***" + "~n " ++ F ++ "~n" + "~n", [megaco:format_timestamp(TS), P, self(), TC | A]), + io:format("~s", [S]), + io:format(user, "~s", [S]); +print2(_, _, _, _) -> ok. p(F, A) -> io:format("*** [~s] ***" "~n " ++ F ++ "~n", - [format_timestamp(now()) | A]). + [megaco:format_timestamp(erlang:timestamp()) | A]). -%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - -random_init() -> - {A,B,C} = now(), - random:seed(A,B,C). -random() -> - 10 * random:uniform(50). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% -apply_load_timer() -> - erlang:send_after(random(), self(), apply_load_timeout). +%% random_init() -> +%% {A,B,C} = erlang:timestamp(), +%% random:seed(A,B,C). +%% random() -> +%% 10 * random:uniform(50). -format_timestamp({_N1, _N2, N3} = Now) -> - {Date, Time} = calendar:now_to_datetime(Now), - {YYYY,MM,DD} = Date, - {Hour,Min,Sec} = Time, - FormatDate = - io_lib:format("~.4w:~.2.0w:~.2.0w ~.2.0w:~.2.0w:~.2.0w 4~w", - [YYYY,MM,DD,Hour,Min,Sec,round(N3/1000)]), - lists:flatten(FormatDate). +%% apply_load_timer() -> +%% erlang:send_after(random(), self(), apply_load_timeout). diff --git a/lib/os_mon/src/disksup.erl b/lib/os_mon/src/disksup.erl index 5118d807e1..4253067c90 100644 --- a/lib/os_mon/src/disksup.erl +++ b/lib/os_mon/src/disksup.erl @@ -264,7 +264,7 @@ check_disk_space({unix, irix}, Port, Threshold) -> Result = my_cmd("/usr/sbin/df -lk",Port), check_disks_irix(skip_to_eol(Result), Threshold); check_disk_space({unix, linux}, Port, Threshold) -> - Result = my_cmd("/bin/df -lk", Port), + Result = my_cmd("/bin/df -lk -x squashfs", Port), check_disks_solaris(skip_to_eol(Result), Threshold); check_disk_space({unix, posix}, Port, Threshold) -> Result = my_cmd("df -k -P", Port), diff --git a/lib/public_key/asn1/CMSAesRsaesOaep.asn1 b/lib/public_key/asn1/CMSAesRsaesOaep.asn1 new file mode 100644 index 0000000000..ca8c7b7f92 --- /dev/null +++ b/lib/public_key/asn1/CMSAesRsaesOaep.asn1 @@ -0,0 +1,39 @@ +CMSAesRsaesOaep {iso(1) member-body(2) us(840) rsadsi(113549) + pkcs(1) pkcs-9(9) smime(16) modules(0) id-mod-cms-aes(19) } + + +DEFINITIONS IMPLICIT TAGS ::= +BEGIN + +-- EXPORTS ALL -- +IMPORTS + -- PKIX + 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)}; + +-- AES information object identifiers -- + +aes OBJECT IDENTIFIER ::= { joint-iso-itu-t(2) country(16) us(840) + organization(1) gov(101) csor(3) nistAlgorithms(4) 1 } + +-- AES using CBC-chaining mode for key sizes of 128, 192, 256 + +id-aes128-CBC OBJECT IDENTIFIER ::= { aes 2 } +id-aes192-CBC OBJECT IDENTIFIER ::= { aes 22 } +id-aes256-CBC OBJECT IDENTIFIER ::= { aes 42 } + +-- AES-IV is a the parameter for all the above object identifiers. + +AES-IV ::= OCTET STRING (SIZE(16)) + + +-- AES Key Wrap Algorithm Identifiers - Parameter is absent + +id-aes128-wrap OBJECT IDENTIFIER ::= { aes 5 } +id-aes192-wrap OBJECT IDENTIFIER ::= { aes 25 } +id-aes256-wrap OBJECT IDENTIFIER ::= { aes 45 } + + +END diff --git a/lib/public_key/asn1/Makefile b/lib/public_key/asn1/Makefile index a920ea87ea..10952106c6 100644 --- a/lib/public_key/asn1/Makefile +++ b/lib/public_key/asn1/Makefile @@ -42,7 +42,7 @@ RELSYSDIR = $(RELEASE_PATH)/lib/public_key-$(VSN) ASN_TOP = OTP-PUB-KEY PKCS-FRAME ASN_MODULES = PKIX1Explicit88 PKIX1Implicit88 PKIX1Algorithms88 \ PKIXAttributeCertificate PKCS-1 PKCS-3 PKCS-7 PKCS-8 PKCS-10 PKCS5v2-0 OTP-PKIX \ - InformationFramework RFC5639 + InformationFramework RFC5639 CMSAesRsaesOaep ASN_ASNS = $(ASN_MODULES:%=%.asn1) ASN_ERLS = $(ASN_TOP:%=%.erl) ASN_HRLS = $(ASN_TOP:%=%.hrl) diff --git a/lib/public_key/asn1/OTP-PUB-KEY.set.asn b/lib/public_key/asn1/OTP-PUB-KEY.set.asn index b3f3ccdb77..7ab1684ff3 100644 --- a/lib/public_key/asn1/OTP-PUB-KEY.set.asn +++ b/lib/public_key/asn1/OTP-PUB-KEY.set.asn @@ -10,3 +10,5 @@ ECPrivateKey.asn1 PKCS-7.asn1 PKCS-10.asn1 RFC5639.asn1 +CMSAesRsaesOaep.asn1 + diff --git a/lib/public_key/doc/src/notes.xml b/lib/public_key/doc/src/notes.xml index d13c9a520a..57d9898661 100644 --- a/lib/public_key/doc/src/notes.xml +++ b/lib/public_key/doc/src/notes.xml @@ -87,6 +87,21 @@ </section> +<section><title>Public_Key 1.6.6.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Support Pasword based encryption with AES</p> + <p> + Own Id: OTP-15870 Aux Id: ERL-952 </p> + </item> + </list> + </section> + +</section> + <section><title>Public_Key 1.6.6</title> <section><title>Improvements and New Features</title> @@ -1247,4 +1262,3 @@ </chapter> - diff --git a/lib/public_key/doc/src/public_key_app.xml b/lib/public_key/doc/src/public_key_app.xml index 923a9f1dfb..5f2c50711a 100644 --- a/lib/public_key/doc/src/public_key_app.xml +++ b/lib/public_key/doc/src/public_key_app.xml @@ -51,6 +51,9 @@ Diffie-Hellman Key Agreement Standard </item> <item>Supports <url href="http://www.ietf.org/rfc/rfc2898.txt"> PKCS-5</url> - Password-Based Cryptography Standard </item> + <item>Supports <url href="http://www.ietf.org/rfc/fc3565.txt"> AES </url> - + Use of the Advanced Encryption Standard (AES) Algorithm in Cryptographic Message Syntax (CMS) + </item> <item>Supports <url href="http://www.ietf.org/rfc/rfc5208.txt"> PKCS-8</url> - Private-Key Information Syntax Standard</item> <item>Supports <url href="http://www.ietf.org/rfc/rfc5967.txt"> PKCS-10</url> - diff --git a/lib/public_key/src/pubkey_pbe.erl b/lib/public_key/src/pubkey_pbe.erl index e6bcedd1b1..6003bf21d0 100644 --- a/lib/public_key/src/pubkey_pbe.erl +++ b/lib/public_key/src/pubkey_pbe.erl @@ -26,9 +26,7 @@ -export([encode/4, decode/4, decrypt_parameters/1, encrypt_parameters/1]). -export([pbdkdf1/4, pbdkdf2/7]). --define(DEFAULT_SHA_MAC_KEYLEN, 20). -define(ASN1_OCTET_STR_TAG, 4). --define(IV_LEN, 8). %%==================================================================== %% Internal application API @@ -41,14 +39,23 @@ %%-------------------------------------------------------------------- encode(Data, Password, "DES-CBC" = Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), - crypto:block_encrypt(des_cbc, Key, IV, pbe_pad(Data, KeyDevParams)); + crypto:block_encrypt(des_cbc, Key, IV, pbe_pad(Data, block_size(des_cbc))); encode(Data, Password, "DES-EDE3-CBC" = Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), <<Key1:8/binary, Key2:8/binary, Key3:8/binary>> = Key, - crypto:block_encrypt(des3_cbc, [Key1, Key2, Key3], IV, pbe_pad(Data)); + crypto:block_encrypt(des3_cbc, [Key1, Key2, Key3], IV, pbe_pad(Data, block_size(des_3ede))); encode(Data, Password, "RC2-CBC" = Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), - crypto:block_encrypt(rc2_cbc, Key, IV, pbe_pad(Data, KeyDevParams)). + crypto:block_encrypt(rc2_cbc, Key, IV, pbe_pad(Data, block_size(rc2_cbc))); +encode(Data, Password, "AES-128-CBC" = Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:block_encrypt(aes_128_cbc, Key, IV, pbe_pad(Data, block_size(aes_128_cbc))); +encode(Data, Password, "AES-192-CBC" = Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:block_encrypt(aes_192_cbc, Key, IV, pbe_pad(Data, block_size(aes_192_cbc))); +encode(Data, Password, "AES-256-CBC"= Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:block_encrypt(aes_256_cbc, Key, IV, pbe_pad(Data, block_size(aes_256_cbc))). %%-------------------------------------------------------------------- -spec decode(binary(), string(), string(), term()) -> binary(). @@ -67,14 +74,16 @@ decode(Data, Password,"RC2-CBC"= Cipher, KeyDevParams) -> crypto:block_decrypt(rc2_cbc, Key, IV, Data); decode(Data, Password,"AES-128-CBC"= Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), - crypto:block_decrypt(aes_cbc128, Key, IV, Data); -decode(Data, Password,"AES-256-CBC"= Cipher, KeyDevParams) -> + crypto:block_decrypt(aes_128_cbc, Key, IV, Data); +decode(Data, Password,"AES-192-CBC"= Cipher, KeyDevParams) -> + {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), + crypto:block_decrypt(aes_192_cbc, Key, IV, Data); +decode(Data, Password,"AES-256-CBC"= Cipher, KeyDevParams) -> {Key, IV} = password_to_key_and_iv(Password, Cipher, KeyDevParams), - crypto:block_decrypt(aes_cbc256, Key, IV, Data). - + crypto:block_decrypt(aes_256_cbc, Key, IV, Data). %%-------------------------------------------------------------------- --spec pbdkdf1(string(), iodata(), integer(), atom()) -> binary(). +-spec pbdkdf1(iodata(), iodata(), integer(), atom()) -> binary(). %% %% Description: Implements password based decryption key derive function 1. %% Exported mainly for testing purposes. @@ -86,7 +95,7 @@ pbdkdf1(Password, Salt, Count, Hash) -> do_pbdkdf1(Result, Count-1, Result, Hash). %%-------------------------------------------------------------------- --spec pbdkdf2(string(), iodata(), integer(), integer(), fun(), atom(), integer()) +-spec pbdkdf2(iodata(), iodata(), integer(), integer(), fun(), atom(), integer()) -> binary(). %% %% Description: Implements password based decryption key derive function 2. @@ -150,17 +159,15 @@ do_pbdkdf1(Prev, Count, Acc, Hash) -> Result = crypto:hash(Hash, Prev), do_pbdkdf1(Result, Count-1 , <<Result/binary, Acc/binary>>, Hash). -iv(#'PBES2-params_encryptionScheme'{algorithm = Algo, - parameters = ASN1IV}) - when (Algo == ?'desCBC') or - (Algo == ?'des-EDE3-CBC') -> - <<?ASN1_OCTET_STR_TAG, ?IV_LEN, IV:?IV_LEN/binary>> = decode_handle_open_type_wrapper(ASN1IV), - IV; iv(#'PBES2-params_encryptionScheme'{algorithm = ?'rc2CBC', parameters = ASN1IV}) -> {ok, #'RC2-CBC-Parameter'{iv = IV}} = 'PKCS-FRAME':decode('RC2-CBC-Parameter', decode_handle_open_type_wrapper(ASN1IV)), - iolist_to_binary(IV). + iolist_to_binary(IV); +iv(#'PBES2-params_encryptionScheme'{algorithm = _Algo, + parameters = ASN1IV}) -> + <<?ASN1_OCTET_STR_TAG, Len:8/unsigned-big-integer, IV:Len/binary>> = decode_handle_open_type_wrapper(ASN1IV), + IV. blocks(1, N, Index, Password, Salt, Count, Prf, PrfHash, PrfLen, Acc) -> <<XorSum:N/binary, _/binary>> = xor_sum(Password, Salt, Count, Index, Prf, PrfHash, PrfLen), @@ -217,17 +224,9 @@ pbe1_oid("RC2-CBC", md5) -> pbe1_oid("DES-CBC", md5) -> ?'pbeWithMD5AndDES-CBC'. -pbe_pad(Data, {#'PBEParameter'{}, _}) -> - pbe_pad(Data); -pbe_pad(Data, #'PBES2-params'{}) -> - pbe_pad(Data); -pbe_pad(Data, _) -> -pbe_pad(Data).%% Data. - - -pbe_pad(Data) -> - N = 8 - (erlang:byte_size(Data) rem 8), - Pad = list_to_binary(lists:duplicate(N, N)), +pbe_pad(Data, BlockSize) -> + N = BlockSize - (erlang:byte_size(Data) rem BlockSize), + Pad = binary:copy(<<N>>, N), <<Data/binary, Pad/binary>>. key_derivation_params(#'PBES2-params'{keyDerivationFunc = KeyDerivationFunc, @@ -249,11 +248,27 @@ key_derivation_params(#'PBES2-params'{keyDerivationFunc = KeyDerivationFunc, pseudo_random_function(#'PBKDF2-params_prf'{algorithm = {_,_, _,'id-hmacWithSHA1'}}) -> {fun crypto:hmac/4, sha, pseudo_output_length(?'id-hmacWithSHA1')}; -pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA1'}) -> - {fun crypto:hmac/4, sha, pseudo_output_length(?'id-hmacWithSHA1')}. +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA1' = Algo}) -> + {fun crypto:hmac/4, sha, pseudo_output_length(Algo)}; +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA224'= Algo}) -> + {fun crypto:hmac/4, sha224, pseudo_output_length(Algo)}; +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA256' = Algo}) -> + {fun crypto:hmac/4, sha256, pseudo_output_length(Algo)}; +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA384' = Algo}) -> + {fun crypto:hmac/4, sha384, pseudo_output_length(Algo)}; +pseudo_random_function(#'PBKDF2-params_prf'{algorithm = ?'id-hmacWithSHA512' = Algo}) -> + {fun crypto:hmac/4, sha512, pseudo_output_length(Algo)}. pseudo_output_length(?'id-hmacWithSHA1') -> - ?DEFAULT_SHA_MAC_KEYLEN. + 20; %%160/8 +pseudo_output_length(?'id-hmacWithSHA224') -> + 28; %%%224/8 +pseudo_output_length(?'id-hmacWithSHA256') -> + 32; %%256/8 +pseudo_output_length(?'id-hmacWithSHA384') -> + 48; %%384/8 +pseudo_output_length(?'id-hmacWithSHA512') -> + 64. %%512/8 derived_key_length(_, Len) when is_integer(Len) -> Len; @@ -266,11 +281,33 @@ derived_key_length(Cipher,_) when (Cipher == ?'rc2CBC') or derived_key_length(Cipher,_) when (Cipher == ?'des-EDE3-CBC') or (Cipher == "DES-EDE3-CBC") -> 24; -derived_key_length(Cipher,_) when (Cipher == "AES-128-CBC") -> + +derived_key_length(Cipher,_) when (Cipher == "AES-128-CBC"); + (Cipher == ?'id-aes128-CBC') -> 16; -derived_key_length(Cipher,_) when (Cipher == "AES-256-CBC") -> +derived_key_length(Cipher,_) when (Cipher == "AES-192-CBC"); + (Cipher == ?'id-aes192-CBC') -> + 24; + +derived_key_length(Cipher,_) when (Cipher == "AES-256-CBC"); + (Cipher == ?'id-aes256-CBC') -> 32. +block_size(Cipher) when Cipher == rc2_cbc; + Cipher == des_cbc; + Cipher == des_3ede -> + 8; +block_size(Cipher) when Cipher == aes_128_cbc; + Cipher == aes_192_cbc; + Cipher == aes_256_cbc -> + 16. + +cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'id-aes128-CBC'}) -> + "AES-128-CBC"; +cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'id-aes192-CBC'}) -> + "AES-192-CBC"; +cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'id-aes256-CBC'}) -> + "AES-256-CBC"; cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'desCBC'}) -> "DES-CBC"; cipher(#'PBES2-params_encryptionScheme'{algorithm = ?'des-EDE3-CBC'}) -> diff --git a/lib/public_key/test/pbe_SUITE.erl b/lib/public_key/test/pbe_SUITE.erl index 1136267411..61db282dfa 100644 --- a/lib/public_key/test/pbe_SUITE.erl +++ b/lib/public_key/test/pbe_SUITE.erl @@ -206,7 +206,10 @@ pbes2() -> [{doc,"Tests encode/decode EncryptedPrivateKeyInfo encrypted with different ciphers using PBES2"}]. pbes2(Config) when is_list(Config) -> decode_encode_key_file("pbes2_des_cbc_enc_key.pem", "password", "DES-CBC", Config), - decode_encode_key_file("pbes2_des_ede3_cbc_enc_key.pem", "password", "DES-EDE3-CBC", Config), + decode_encode_key_file("pbes2_des_ede3_cbc_enc_key.pem", "password", "DES-EDE3-CBC", Config), + decode_encode_key_file("pbes2_aes_128_enc_key.pem", "password", "AES-128-CBC", Config), + decode_encode_key_file("pbes2_aes_192_enc_key.pem", "password", "AES-192-CBC", Config), + decode_encode_key_file("pbes2_aes_256_enc_key.pem", "password", "AES-256-CBC", Config), case lists:member(rc2_cbc, proplists:get_value(ciphers, crypto:supports())) of true -> decode_encode_key_file("pbes2_rc2_cbc_enc_key.pem", "password", "RC2-CBC", Config); @@ -239,7 +242,6 @@ decode_encode_key_file(File, Password, Cipher, Config) -> {ok, PemKey} = file:read_file(filename:join(Datadir, File)), PemEntry = public_key:pem_decode(PemKey), - ct:pal("Pem entry: ~p" , [PemEntry]), [{Asn1Type, _, {Cipher,_} = CipherInfo} = PubEntry] = PemEntry, #'RSAPrivateKey'{} = KeyInfo = public_key:pem_entry_decode(PubEntry, Password), PemKey1 = public_key:pem_encode([public_key:pem_entry_encode(Asn1Type, KeyInfo, {CipherInfo, Password})]), diff --git a/lib/public_key/test/pbe_SUITE_data/pbes2_aes_128_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_128_enc_key.pem new file mode 100644 index 0000000000..5702119ad6 --- /dev/null +++ b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_128_enc_key.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIWrPgmqJqNpICAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAECBBA/bbIMYqQMUDxMk9ifPR7ABIIE +0Drfqke1/ccFxk786hTh36yjVo48Xx7B3Scb92KtmyQpNaR6GbR+jhP9cxIcvmGN +YroCB896VJSIx8PraqGgIJ1hblZXyfLanB0mUnZvaaQ4xp3UJT53a0yOm5Lfd+fB +0TyaoEzca2jA5EVVh3yH6gzNsvQJRw6cQP5CAptLjiUv2jrwVGnO8x8X4egJDLZS +Sb8B5AW8h1sGsyKEEFto6gpBjVqnVn5veMoI/Cfs9qDr071+dhbps/m6pseKKp0z +8qeFM7+9Y4npD1VYg2gqOFi19QAI3gwq6tC8grOzRA8dPFUgpV9eMToVsI2OFQc1 +xnFZEV7NZVymh5HjKM1jwFy6es+5TFoMtRu6vDxKS6Y13lIlZ4oQSh8aXtG5Ylt2 +CqsKNHyDbZUpvKe/k19TBmVXQBCYFuN733jI9/4JBtpygnxwt1aXCvq/PFFGsTS4 +p1JOQvr/jaD7b4JO6IMXH1kSVxiMXKXNG7wPUNr6OWJvc7OqdclsZa7ibEx4L52x +DuFmsxQo4a3iibhbcjr436OmR5Uw2UAstB5qxWfMhkt+e7rRhCOh/3O7SAYEpt+f +Zr2VFXdGme4kR6uMCzgGiSh0qCseQXpJUZVufn/Go9r+601OJTJIQ9a2VoqlMR8o +Dd14D0gBXXaZkY60Mh8iXR/MjKDuv0KBUyBzfcpk3fLmv0PhGSkbn6j+q1jZbogm +EhI0AL5s2EoofuBdvgdusBhCrrwCMonprqR7BuaKPD0GEw5utnT5ovcUg/sjMJox +10100QwAzQScU4iG/xic/TsN+ZMumhUcYs003MsZkRLvCEFxZurEMx7819CqfhIc +NGd7ETTBSwoNf5pXRTHaTbW6pPiIeWunLUUVsRcNoBtL/cXmg+mu1zdsD7nD51mJ +vG9A7LPW7XVl2Jv2NgQoKkHYO7cVozmcz6AE2z1q+XN4LGto8JEZktb6E7UIyXXg +Ls4Tv0sn5TLgtaJ31w4+9iybNiGoVYOc4h0s5DoNR4ivcZ6n/Qnf8PTrNzejEJY6 +R/UnDbc24u0palGc1kei99d0BYodnq4OlAj7M7ML0GncftInhgA0Dp81YG5PujMa +irhvwtnD5Xysfh1YrroAEN7Qxc8+2JlpgNSFlFFkMgfibc6jvTX6/C6MaFz8hiOq +W43ZBEzjMIs23ZrJKOJGsuTdHSob+VbvqIMgS2PeGb/6g3/GjdipCbynNhX3zUOM +3j/lpZOiAwE/Bftr5FOSfTFpnyorIIeyWgROEZTTL4eSYvnBjzf+tUdXY7ltxJie +q0rpQ42X7+B4gTo8Qj/xC7LXSCldERK57cCwwITvjcHwxPyOiJ9BMI1HlRQ/Fo3C +lPYIst1xjJ67qrTm6mWkor2hUOZcg4MOOzXWuijWRGJ/Wz0H+GKWtoE2X536D6sy +a4Nwwj09oFY4Fph/SUNwy0MLpTSzikpUx6mxjbs3Odvo6tWWVcicp/dCWYCqLpGU +3axEb/qlsaRNtKJg9O3Fq7hh1BTyLNGB2ET5wSKtlSD0bDeF15bBvkHB3z2/lDls +YQ2hEHMjeSEZZyGTPqEHwtBuUwiWBBXwOIhT8nfYXbHWR0CLBLth2+E/JCaO9hD2 +V277arqNFa8nugZMwS+ragi6vbgIX4BiS/rnfYXgqaxD +-----END ENCRYPTED PRIVATE KEY----- diff --git a/lib/public_key/test/pbe_SUITE_data/pbes2_aes_192_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_192_enc_key.pem new file mode 100644 index 0000000000..ee82e9f667 --- /dev/null +++ b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_192_enc_key.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQIcqBCM7v+ZlkCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEWBBD93r4IWBhvry+cdfwIDOKeBIIE +0DXM8S70sMsUmwxRZQtKwGfYddEWIc9lrEdsgEEuonF6NrseRq7QdXnBSPwq5f0O +ofMZ/0OCun3Qg1ls1EdsyKdijSOq27ZhHCnmWi1Rw1ApJIAq5i/jY8U17+lUakvG +VtcsuRzlKmFxbBW44kLK7vK6xiA76HPx0I4ZXcdywR0pbLT1ubbhbQ9djLnBiYkT +odszGTyxNceEse1Hu/RhFK17tnwov0fdioKY2i9F7qfq8lYLPrusEKTY7tOVjFOh +bXeCry1BL0KTt65JVGR9xQCI0qokEU0QrCgD6skq7Vx2C/Ho1sW6h8FBFVIm6ozO +bEUtVk3Xgs5yieetha1GxJAang1VxAPemnXfOmVapoSgSv1BQyDdnk3067Sfkh64 +A5yf44BUjvJsSd/ViCVmCryoXU7KOMAdFkyRSiDDLQus6bZGEhc6f+VEikG+TZ2L +xxY4OucE2Bz67S6ycyOUpXKo0+FW0juE6NTJdlYSXWOvfciZKA83h6yAej6MfUEu +4orIvnCTVO7i3+hHybnSgftj42jrqqZzeXll8rkGHg4syrKRVaDD6qfJjgAHBJkJ +pZT4zZwuJ1puWfBykI25S4mKUnk0erq4N5jpGqdm7U14fWBWCjZN85jY4WgZZOJx +kBNO2NbmZKzZEzRGyMJ563z4l7MNfzZBHv+FeBNkX146J4ZhMbT8IXPGV9peNWqu +mY2B9RhN4hlDrd3Hfz5uiiF3UGrFkDcsPRBHWGqQ20YpuOQNno7iL8N0FWauERw1 +dvxAGVwFfUznR3wc/eyGcnRhqQhlYPspukh0IVIyEbre3yVFSG/41GQYQfg08XYd +LYiiDUu1i515/GeDvYN5VcnZ4nMhPgqfxW4rEUZjI86p++bqwqGy8eOCivkzGV3A +IFWQwlvKKzU7tSdi3uHUq5v7xQsJrALdf67JVjCCGfUZa17O41vmm58L/vKhhL2Y +mLz/H004DPsB+CtWoLwqZ8Jmb1EHwqNbna3tGHn3n63j2cV7gykZFa/zXeuBbbJ/ +t4ZIojIEzwAVKA9Xzcl3wyGCRr62WJPEcOqe4kBYREuKd22juPEm9RQgciIIj0tP +eJVpD0QarGGzERsaq7pheAiWisO+Q4cLjF8Mb3/r89abnd4AQk6meabFJIE2dXWp +LZy3I6FkNQ7L7LxNOILhnaWzWGdOBVwHeAAxfbLOzM22ewj7oUwBCRpsBJ8zl2PL +VhUjX6N26YoiR9gE1RBaVrwRkYLmkyGvrowCDoZVPxvJqbfIESQE42zGB9DbEPNp +WXCnzAg5cIjNC31We274yLE7dpNPVRXPJCRhtp7noorWVzDdKB+dFvg08bIir6Vj +1gxy8DvuZE1Gq9vqx38V7Cy2MrSpsgapw5mli4n5cMafE7Ty3j5pBJFF2f3jUn6B +7MjCrKp1d8v6MEy18J/Ugu1Lytb92LMcNtWBKmqyCSxekrUB9/FC2hWqOpdwRI6q +QMWkwshjyEhmlr2PAkBPM4uVzUFc9lBw1GzOUChkr9jiINdbsUSRJrwZ32Nc3gRY +yKzWbEELPSgRcXwXgH3QqZukvmk2tBMTIxilXqKTLmd7t/AEnIhkbqC0pfnyChyU +YlFkme0RpAXpgbDJgv+Vk+1/1s6gyaNSzT4s2Q340WIO +-----END ENCRYPTED PRIVATE KEY----- diff --git a/lib/public_key/test/pbe_SUITE_data/pbes2_aes_256_enc_key.pem b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_256_enc_key.pem new file mode 100644 index 0000000000..050337aead --- /dev/null +++ b/lib/public_key/test/pbe_SUITE_data/pbes2_aes_256_enc_key.pem @@ -0,0 +1,30 @@ +-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFLTBXBgkqhkiG9w0BBQ0wSjApBgkqhkiG9w0BBQwwHAQI4MxgpDiHxQcCAggA +MAwGCCqGSIb3DQIJBQAwHQYJYIZIAWUDBAEqBBA2g/L8XmlK2axDkeYJCltnBIIE +0C3+NQ93DzEK/9qicy1sj0Vag1M7AeJjTGGpatETCxM+eHjk4kNNeDeMV5+EmCSu +Db4P48uvHOBGGCcqdjnQovfQsAh81GWxgF3yqpd4OKn2RubMLO4/Qu+zGtt/XRKz +T0pyHHBu6hyPSOhad2SIjKWuaHepwxGYaejLP83sy6yhm0sEmyBUn4nGSTOROcqR +wd7EbwU2PYUcrRGGxtChU7MUNt48wBO50Xmri1ssPPtZV6MHio4IoIz4hqzCjvAc +VE1BqAvNIJ7icpdnL8Jqq0lfwEmGjFCkAjgov5fNW9I1b44jE2Tv5LM2urMH8InQ +9qNjTHozYQhHAk9nX4cmMgHsIhkOd7Z2M+nz8Hd1tj9DmBNOr5XbfyctgVntaMB4 +GGnThuNlX8d5giOKOcaNPMpLU1jtfDcb73mEhwCYcdo1PM0rjrYZ7qetjXJW/oHs +Nl/hIZIRpMuCRVuXHml4G+ziKbMnXUN8sbtvgkQatYFHFQOhAqZeyzWp8SlDcfqb +Zt0LlZVJEhKUYzZgKoe7SmR1rXTTCfYeB75PddyYwVgf/IkT6HJ/y1apGOP6/UJ8 +7UV6zssQA35gMsYDT36sH2hAQvA/cOFxSxrip0gm0xXOeFF0gbyZWbFqk0aULaeF +rbBoMe28akxdE4eD06b+TP2NguUGP72l3TPOlG4PQVScweMw9L3oPXOVj4Vbbd0y +DenNvRHlWIwOh/y7ADTHSWq9CE45QDBvFaTcn43JQWD8xCmhAhI/9H+fhAQUhABm +P5QoJLE2IGo8A+Gi7rfgYQb3fCgqcn8azsRJzozhE+oXxMvxEESejYTtm26FNmLg +ONTWysF9BiaKHt2IXwRX97691wZqv5wJEaxeeJxfVQ6MlAHoEDXe49VxGN4zFXuq +Yb71JdQDgM94jwc/PoUwFH2ALSkIciiKwU0xfFpptycl4qWpy9m7QTIKw0DjgCfg +MuySPRGM5jn3yVg72ux2Qf9MKNEybWjZ+Se9MJ1IZmZK5eOo6L2JsFCc0nRn908E +vn4gAgUfMxyCZ1ygXfxINVAixR+6KPHsz1QTIxTZkrlnXRsuEu1ZfBSHzmXESvJo +3I9PkP/Iekg1FBpB5xxd7mXwCj17EWqYXWsLnfd8SblMjRYd64q7hfx0oU/MJ1wi +KadkGcyAGVRyleJRBR0LleYj/2sDihrRQY4zu5UtzSMFMH0XWjSWk5+ZQb+z3iDc +Ud4GHcHiuTMH+i03ApZGWLN9v93za/15fsnZogstgJkaHxizTz5JuCkRf15xd8+O +EH77Tsfizjp+h2NF/wcr4OSD0i+H0mwZWajpZ3UmSeJ0BFK6ODEbmVycrInpHo3n +zyMJnEDTJXL3HUwZSLjO5e5cNaB+75tdHrj2yJtRLuaJFr02b0EO1MUYfuUuqlK4 +7mg7FkBsimW+CXkoLRjHYK88ibT3G+rZ/STf4S/jxiRjBi06FAql3H02K5i1umgB +0BaaQei0Z8wQxMeTEnGzL+OcJeqDA1ZRFeXe7DNGsX1jeTYKPHA/Dr2IdZqyiCr2 +xh6e7RJuUe4D2liXW8LlMdwhN/7xSinA031PgBmb8XzSRmfdHhytFkA8PiM5T2ew +NR3qXBJ/G7BuRa/t26RuKI3BMVoBQPhGx80ds10uJjxq +-----END ENCRYPTED PRIVATE KEY----- diff --git a/lib/sasl/src/Makefile b/lib/sasl/src/Makefile index fd62588f5c..a5b9a5e731 100644 --- a/lib/sasl/src/Makefile +++ b/lib/sasl/src/Makefile @@ -63,9 +63,6 @@ TARGET_FILES= $(MODULES:%=$(EBIN)/%.$(EMULATOR)) $(APP_TARGET) $(APPUP_TARGET) # ---------------------------------------------------- ERL_COMPILE_FLAGS += -I../../stdlib/include -Werror -ifeq ($(USE_ESOCK), yes) -ERL_COMPILE_FLAGS += -DUSE_ESOCK=true -endif # ---------------------------------------------------- diff --git a/lib/sasl/src/systools_make.erl b/lib/sasl/src/systools_make.erl index b5a6b44f93..c5d4b9bef3 100644 --- a/lib/sasl/src/systools_make.erl +++ b/lib/sasl/src/systools_make.erl @@ -47,9 +47,11 @@ -compile({inline,[{badarg,2}]}). -ifdef(USE_ESOCK). --define(ESOCK_MODS, [socket]). +-define(ESOCK_SOCKET_MODS, [socket]). +-define(ESOCK_NET_MODS, [prim_net]). -else. --define(ESOCK_MODS, []). +-define(ESOCK_SOCKET_MODS, []). +-define(ESOCK_NET_MODS, []). -endif. @@ -1573,8 +1575,8 @@ preloaded() -> [atomics,counters,erl_init,erl_prim_loader,erl_tracer,erlang, erts_code_purger,erts_dirty_process_signal_handler, erts_internal,erts_literal_area_collector, - init,net,persistent_term,prim_buffer,prim_eval,prim_file, - prim_inet,prim_zip] ++ ?ESOCK_MODS ++ [zlib]. + init,persistent_term,prim_buffer,prim_eval,prim_file, + prim_inet] ++ ?ESOCK_NET_MODS ++ [prim_zip] ++ ?ESOCK_SOCKET_MODS ++ [zlib]. %%______________________________________________________________________ %% Kernel processes; processes that are specially treated by the init diff --git a/lib/snmp/Makefile b/lib/snmp/Makefile index 879f1b05c5..df321fc2d1 100644 --- a/lib/snmp/Makefile +++ b/lib/snmp/Makefile @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 1996-2016. All Rights Reserved. +# Copyright Ericsson AB 1996-2019. 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. @@ -136,11 +136,17 @@ dclean: dialyzer_plt: $(DIA_PLT) -$(DIA_PLT): +$(DIA_PLT): Makefile @echo "Building $(APPLICATION) plt file" @dialyzer --build_plt \ --output_plt $@ \ -r ../$(APPLICATION)/ebin \ + ../../lib/kernel/ebin \ + ../../lib/stdlib/ebin \ + ../../lib/runtime_tools/ebin \ + ../../lib/crypto/ebin \ + ../../lib/mnesia/ebin \ + ../../erts/preloaded/ebin \ --output $(DIA_ANALYSIS) \ --verbose @@ -148,4 +154,4 @@ dialyzer: $(DIA_PLT) @echo "Running dialyzer on $(APPLICATION)" @dialyzer --plt $< \ ../$(APPLICATION)/ebin \ - --verbose
\ No newline at end of file + --verbose diff --git a/lib/snmp/doc/src/snmpa_mib_storage.xml b/lib/snmp/doc/src/snmpa_mib_storage.xml index ee2b009e77..6db2f178a9 100644 --- a/lib/snmp/doc/src/snmpa_mib_storage.xml +++ b/lib/snmp/doc/src/snmpa_mib_storage.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>2013</year><year>2016</year> + <year>2013</year><year>2019</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -193,7 +193,7 @@ </func> <func> - <name since="OTP R16B01">Module:match_object(TabId, Pattern) -> {ok, Recs} | {error, Reason}</name> + <name since="OTP R16B01">Module:match_object(TabId, Pattern) -> Recs | {error, Reason}</name> <fsummary>Search the mib-storage table for record matching pattern</fsummary> <type> <v>TabId = term()</v> @@ -210,7 +210,7 @@ </func> <func> - <name since="OTP R16B01">Module:match_delete(TabId, Pattern) -> {ok, Recs} | {error, Reason}</name> + <name since="OTP R16B01">Module:match_delete(TabId, Pattern) -> Recs | {error, Reason}</name> <fsummary>Delete records in the mib-storage table matching pattern</fsummary> <type> <v>TabId = term()</v> diff --git a/lib/snmp/src/agent/snmp_community_mib.erl b/lib/snmp/src/agent/snmp_community_mib.erl index 9fd7b30f9f..4bd30632f5 100644 --- a/lib/snmp/src/agent/snmp_community_mib.erl +++ b/lib/snmp/src/agent/snmp_community_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. 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. @@ -206,10 +206,10 @@ do_add_community(Community) -> {error, create_failed} end catch - {error, Reason} -> - {error, Reason}; - Class:Reason -> - {error, {Class, Reason, erlang:get_stacktrace()}} + throw:{error, _} = ERROR -> + ERROR; + C:E:S -> + {error, {C, E, S}} end. %% FIXME: does not work with mnesia @@ -545,26 +545,18 @@ snmpTargetAddrExtTable(is_set_ok, RowIndex, Cols0) -> end. - get_snmpTargetAddrTDomain(RowIndex, Col) -> - case - get( - snmpTargetAddrTable, RowIndex, - [?snmpTargetAddrRowStatus,?snmpTargetAddrTDomain]) - of - [{value,?snmpTargetAddrRowStatus_active},ValueTDomain] -> - case ValueTDomain of - {value,TDomain} -> - TDomain; - _ -> - ?snmpUDPDomain - end; - _ -> + Cols = [?snmpTargetAddrRowStatus,?snmpTargetAddrTDomain], + case snmp_target_mib:snmpTargetAddrTable(get, RowIndex, Cols) of + [{value, ?snmpTargetAddrRowStatus_active}, {value, TDomain}] -> + TDomain; + [{value, ?snmpTargetAddrRowStatus_active}, _] -> + ?snmpUDPDomain; + _ -> wrongValue(Col) end. - verify_snmpTargetAddrExtTable_cols([], _TDomain, Cols) -> {ok, lists:reverse(Cols)}; verify_snmpTargetAddrExtTable_cols([{Col, Val0}|Cols], TDomain, Acc) -> diff --git a/lib/snmp/src/agent/snmp_framework_mib.erl b/lib/snmp/src/agent/snmp_framework_mib.erl index 7ea4f0ed97..6db6f87a85 100644 --- a/lib/snmp/src/agent/snmp_framework_mib.erl +++ b/lib/snmp/src/agent/snmp_framework_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2016. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. 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. @@ -246,6 +246,7 @@ check_agent(X) -> %% Ordering function to sort intAgentTransportDomain first %% hence before intAgentIpAddress. Sort other entries on the key. +-dialyzer({nowarn_function, order_agent/2}). order_agent(EntryA, EntryB) -> snmp_conf:keyorder( 1, EntryA, EntryB, diff --git a/lib/snmp/src/agent/snmp_generic.erl b/lib/snmp/src/agent/snmp_generic.erl index e67a1b3c80..26a0dd0648 100644 --- a/lib/snmp/src/agent/snmp_generic.erl +++ b/lib/snmp/src/agent/snmp_generic.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2017. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. 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. @@ -421,12 +421,12 @@ table_check_status(NameDb, Col, ?'RowStatus_createAndGo', RowIndex, Cols) -> _Found -> {inconsistentValue, Col} end catch - _:_Reason -> + _:_E:_S -> ?vtrace( "failed construct row (createAndGo): " - " n Reason: ~p" - " n Stack: ~p", - [_Reason, erlang:get_stacktrace()]), + " n Error: ~p" + " n Stack: ~p", + [_E, _S]), {noCreation, Col} % Bad RowIndex end; true -> {inconsistentValue, Col} @@ -441,12 +441,12 @@ table_check_status(NameDb, Col, ?'RowStatus_createAndWait', RowIndex, Cols) -> _Row -> {noError, 0} catch - _:_Reason -> + _:_E:_S -> ?vtrace( "failed construct row (createAndWait): " - " n Reason: ~p" - " n Stack: ~p", - [_Reason, erlang:get_stacktrace()]), + " n Error: ~p" + " n Stack: ~p", + [_E, _S]), {noCreation, Col} % Bad RowIndex end; true -> {inconsistentValue, Col} diff --git a/lib/snmp/src/agent/snmp_standard_mib.erl b/lib/snmp/src/agent/snmp_standard_mib.erl index bfe471178d..679d2657c6 100644 --- a/lib/snmp/src/agent/snmp_standard_mib.erl +++ b/lib/snmp/src/agent/snmp_standard_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2015. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. 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. @@ -27,6 +27,7 @@ -include("snmp_types.hrl"). -include("STANDARD-MIB.hrl"). +-include("snmpa_internal.hrl"). -define(VMODULE,"STANDARD-MIB"). -include("snmp_verbosity.hrl"). @@ -547,10 +548,12 @@ dummy(_Op) -> ok. %%----------------------------------------------------------------- snmp_set_serial_no(new) -> snmp_generic:variable_func(new, {snmpSetSerialNo, volatile}), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - Val = random:uniform(2147483648) - 1, + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + Val = rand:uniform(2147483648) - 1, snmp_generic:variable_func(set, Val, {snmpSetSerialNo, volatile}); snmp_set_serial_no(delete) -> diff --git a/lib/snmp/src/agent/snmp_target_mib.erl b/lib/snmp/src/agent/snmp_target_mib.erl index e65fa7f340..22fd3acb84 100644 --- a/lib/snmp/src/agent/snmp_target_mib.erl +++ b/lib/snmp/src/agent/snmp_target_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2015. All Rights Reserved. +%% Copyright Ericsson AB 1998-2019. 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. @@ -42,6 +42,7 @@ -define(VMODULE,"TARGET-MIB"). -include("snmp_verbosity.hrl"). +-include("snmpa_internal.hrl"). %% Column not accessible via SNMP - needed when the agent sends informs @@ -673,10 +674,12 @@ snmpTargetSpinLock(print) -> snmpTargetSpinLock(new) -> snmp_generic:variable_func(new, {snmpTargetSpinLock, volatile}), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - Val = random:uniform(2147483648) - 1, + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + Val = rand:uniform(2147483648) - 1, snmp_generic:variable_func(set, Val, {snmpTargetSpinLock, volatile}); snmpTargetSpinLock(delete) -> diff --git a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl index f6e4fd3951..4842669fa4 100644 --- a/lib/snmp/src/agent/snmp_user_based_sm_mib.erl +++ b/lib/snmp/src/agent/snmp_user_based_sm_mib.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2015. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. 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. @@ -440,10 +440,12 @@ usmUserSpinLock(print) -> usmUserSpinLock(new) -> snmp_generic:variable_func(new, {usmUserSpinLock, volatile}), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - Val = random:uniform(2147483648) - 1, + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + Val = rand:uniform(2147483648) - 1, snmp_generic:variable_func(set, Val, {usmUserSpinLock, volatile}); usmUserSpinLock(delete) -> diff --git a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl index c6eeb7cea2..a5a65d9326 100644 --- a/lib/snmp/src/agent/snmp_view_based_acm_mib.erl +++ b/lib/snmp/src/agent/snmp_view_based_acm_mib.erl @@ -48,6 +48,7 @@ -include("SNMPv2-TC.hrl"). -include("SNMP-VIEW-BASED-ACM-MIB.hrl"). -include("snmpa_vacm.hrl"). +-include("snmpa_internal.hrl"). -define(VMODULE,"VACM-MIB"). @@ -653,7 +654,7 @@ vacmAccessTable(is_set_ok, RowIndex, Cols0) -> {{Col, ?'RowStatus_createAndWait'}, _} -> %% Row already exists => inconsistentValue {inconsistentValue, Col}; - {value, {_Col, ?'RowStatus_destroy'}} -> + {{_Col, ?'RowStatus_destroy'}, _} -> %% always ok! {noError, 0}; {_, false} -> @@ -860,10 +861,12 @@ vacmViewSpinLock(print) -> vacmViewSpinLock(new) -> snmp_generic:variable_func(new, volatile_db(vacmViewSpinLock)), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - Val = random:uniform(2147483648) - 1, + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + Val = rand:uniform(2147483648) - 1, snmp_generic:variable_func(set, Val, volatile_db(vacmViewSpinLock)); vacmViewSpinLock(delete) -> @@ -1112,9 +1115,7 @@ externalize_next(Name, Result) when is_list(Result) -> F = fun({[Col | _] = Idx, Val}) -> {Idx, externalize(Name, Col, Val)}; (Other) -> Other end, - [F(R) || R <- Result]; -externalize_next(_, Result) -> - Result. + [F(R) || R <- Result]. externalize_get(Name, Cols, Result) when is_list(Result) -> @@ -1124,9 +1125,7 @@ externalize_get(Name, Cols, Result) when is_list(Result) -> end, %% Merge column numbers and return values. there must be as much %% return values as there are columns requested. And then patch all values - [F(R) || R <- lists:zip(Cols, Result)]; -externalize_get(_, _, Result) -> - Result. + [F(R) || R <- lists:zip(Cols, Result)]. externalize(vacmViewTreeFamilyTable, ?vacmViewTreeFamilyMask, Val) -> imask2emask(Val); diff --git a/lib/snmp/src/agent/snmpa_agent.erl b/lib/snmp/src/agent/snmpa_agent.erl index f280260f47..7489f74223 100644 --- a/lib/snmp/src/agent/snmpa_agent.erl +++ b/lib/snmp/src/agent/snmpa_agent.erl @@ -525,9 +525,25 @@ unregister_subagent(Agent, SubagentOidOrPid) -> %% These subagent_ functions either return a value, or exits %% with {nodedown, Node} | Reason. %%----------------------------------------------------------------- -subagent_get(SubAgent, Varbinds, IsNotification) -> + +%% A proper spec for this would be something like this: +%% But, there is now way to spec that a process *can* exit. +%% -spec subagent_get(Agent, VBs, IsNotification) -> +%% {noError, 0, NewVBs} | +%% {ErrStatus, ErrIndex, []} | +%% erlang:exit(Reason) when +%% Agent :: pid(), +%% VBs :: [snmp:varbind()], +%% IsNotification :: boolean(), +%% NewVBs :: [snmp:varbind()], +%% ErrStatus :: snmp:error_status(), +%% ErrIndex :: snmp:error_index(), +%% Reason :: {nodedown, Node} | term(), +%% Node :: atom(). + +subagent_get(SubAgent, VBs, IsNotification) -> PduData = get_pdu_data(), - call(SubAgent, {subagent_get, Varbinds, PduData, IsNotification}). + call(SubAgent, {subagent_get, VBs, PduData, IsNotification}). subagent_get_next(SubAgent, MibView, Varbinds) -> PduData = get_pdu_data(), diff --git a/lib/snmp/src/agent/snmpa_authentication_service.erl b/lib/snmp/src/agent/snmpa_authentication_service.erl index e4238a8384..b6b9f5bd96 100644 --- a/lib/snmp/src/agent/snmpa_authentication_service.erl +++ b/lib/snmp/src/agent/snmpa_authentication_service.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -19,13 +19,27 @@ %% -module(snmpa_authentication_service). --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{init_check_access, 2}]; -behaviour_info(_) -> - undefined. - +-export_type([ + acm_data/0 + ]). + +-type acm_data() :: {community, + SecModel :: 0 | 1 | 2 | 3, % any | v1 | v2c | v3 + Community :: string(), + %% Oids for either: + %% transportDomainUdpIpv4 | transportDomainUdpIpv6 + TDomain :: snmp:oid(), + TAddress :: [non_neg_integer()]} | + {v3, + MsgID :: integer(), + SecModel :: 0 | 1 | 2 | 3, % any | v1 | v2c | v3 + SecName :: string(), + %% noAuthNoPriv | authNoPriv | authPriv + SecLevel :: 1 | 2 | 3, + ContextEngineID :: string(), + ContextName :: string(), + SecData :: term()}. + %%----------------------------------------------------------------- %% init_check_access(Pdu, ACMData) @@ -46,9 +60,7 @@ behaviour_info(_) -> %% Variable = snmpInBadCommunityNames | %% snmpInBadCommunityUses | %% snmpInASNParseErrs -%% Reason = snmp_message_decoding | -%% {bad_community_name, Address, Community}} | -%% {invalid_access, Access, Op} +%% Reason = {bad_community_name, Address, Community}} %% %% Purpose: Called once for each Pdu. Returns a MibView %% which is later used for each variable in the pdu. @@ -57,3 +69,14 @@ behaviour_info(_) -> %% %% NOTE: This function is executed in the Master agents's context %%----------------------------------------------------------------- + +-callback init_check_access(Pdu, ACMData) -> + {ok, MibView, ContextName} | + {error, Reason} | + {discarded, Variable, Reason} when + Pdu :: snmp:pdu(), + ACMData :: acm_data(), + MibView :: snmp_view_based_acm_mib:mibview(), + ContextName :: string(), + Reason :: term(), + Variable :: snmpInBadCommunityNames. diff --git a/lib/snmp/src/agent/snmpa_conf.erl b/lib/snmp/src/agent/snmpa_conf.erl index fc5116dac9..c2e9d4025a 100644 --- a/lib/snmp/src/agent/snmpa_conf.erl +++ b/lib/snmp/src/agent/snmpa_conf.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2016. All Rights Reserved. +%% Copyright Ericsson AB 2006-2019. 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. @@ -83,6 +83,34 @@ +-export_type([ + usm_entry/0 + ]). + +-type usm_entry() :: { + EngineID :: string(), + UserName :: string(), + SecName :: string(), + Clone :: zeroDotZero | [non_neg_integer()], + AuthP :: usmNoAuthProtocol | + usmHMACMD5AuthProtocol | + usmHMACSHAAuthProtocol, + AuthKeyC :: string(), + OwnAuthKeyC :: string(), + PrivP :: usmNoPrivProtocol | + usmDESPrivProtocol | + usmAesCfb128Protocol, + PrivKeyC :: string(), + OwnPrivKeyC :: string(), + Public :: string(), + %% Size 16 for usmHMACMD5AuthProtocol + %% Size 20 for usmHMACSHAAuthProtocol + AuthKey :: [non_neg_integer()], + %% Size 16 for usmDESPrivProtocol | usmAesCfb128Protocol + PrivKey :: [non_neg_integer()] + }. + + %% %% ------ agent.conf ------ %% diff --git a/lib/snmp/src/agent/snmpa_discovery_handler.erl b/lib/snmp/src/agent/snmpa_discovery_handler.erl index ffdd6aca1e..6fb1d1eb72 100644 --- a/lib/snmp/src/agent/snmpa_discovery_handler.erl +++ b/lib/snmp/src/agent/snmpa_discovery_handler.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2009-2016. All Rights Reserved. +%% Copyright Ericsson AB 2009-2019. 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. @@ -19,12 +19,17 @@ %% -module(snmpa_discovery_handler). --export([behaviour_info/1, verify/1]). - -behaviour_info(callbacks) -> - [{stage1_finish, 3}]; -behaviour_info(_) -> - undefined. +-export([verify/1]). +-callback stage1_finish(TargetName, ManagerEngineID, ExtraInfo) -> + ignore | + {ok, snmpa_conf:usm_entry() | [snmpa_conf:usm_entry()]} | + {ok, snmpa_conf:usm_entry() | [snmpa_conf:usm_entry()], NewExtraInfo} when + TargetName :: string(), + ManagerEngineID :: string(), + ExtraInfo :: term(), + NewExtraInfo :: term(). + verify(Mod) -> snmp_misc:verify_behaviour(?MODULE, Mod). + diff --git a/lib/snmp/src/agent/snmpa_error_report.erl b/lib/snmp/src/agent/snmpa_error_report.erl index 8f28eac653..6b281693e5 100644 --- a/lib/snmp/src/agent/snmpa_error_report.erl +++ b/lib/snmp/src/agent/snmpa_error_report.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -19,10 +19,12 @@ %% -module(snmpa_error_report). --export([behaviour_info/1]). +-callback config_err(Format, Args) -> + snmp:void() when + Format :: string(), + Args :: [term()]. -behaviour_info(callbacks) -> - [{user_err, 2}, - {config_err, 2}]; -behaviour_info(_) -> - undefined. +-callback user_err(Format, Args) -> + snmp:void() when + Format :: string(), + Args :: [term()]. diff --git a/lib/snmp/src/agent/snmpa_get.erl b/lib/snmp/src/agent/snmpa_get.erl index e67975a67d..8b16016d84 100644 --- a/lib/snmp/src/agent/snmpa_get.erl +++ b/lib/snmp/src/agent/snmpa_get.erl @@ -75,6 +75,9 @@ %% {ErrorStatus, ErrorIndex, []} %%----------------------------------------------------------------- +%% There is now to properly spec the behaviour of the ?AGENT:subagent_get/3 +%% function (it *can* exit). +-dialyzer({nowarn_function, do_get/3}). do_get(UnsortedVarbinds, IsNotification, _Extra) -> {MyVarbinds, SubagentVarbinds} = ?LIB:agent_sort_vbs(UnsortedVarbinds), case do_get_local(MyVarbinds, IsNotification) of @@ -122,6 +125,7 @@ do_get(MibView, UnsortedVarbinds, IsNotification, Extra) -> do_get_local(VBs, IsNotification) -> do_get_local(VBs, [], IsNotification). +-dialyzer({nowarn_function, do_get_local/3}). do_get_local([Vb | Vbs], Res, IsNotification) -> case try_get(Vb, IsNotification) of NewVb when is_record(NewVb, varbind) -> @@ -144,11 +148,16 @@ do_get_local([], Res, _IsNotification) -> %% Returns: {noError, 0, ListOfNewVarbinds} | %% {ErrorStatus, ErrorIndex, []} %%----------------------------------------------------------------- + +%% There is now to properly spec the behaviour of the ?AGENT:subagent_get/3 +%% function (it *can* exit). +-dialyzer({nowarn_function, do_get_subagents/3}). do_get_subagents(SubagentVarbinds, IsNotification) -> do_get_subagents(SubagentVarbinds, [], IsNotification). + do_get_subagents([{SubAgentPid, SAVbs} | Tail], Res, IsNotification) -> {_SAOids, Vbs} = ?LIB:sa_split(SAVbs), - case catch ?AGENT:subagent_get(SubAgentPid, Vbs, IsNotification) of + case (catch ?AGENT:subagent_get(SubAgentPid, Vbs, IsNotification)) of {noError, 0, NewVbs} -> do_get_subagents(Tail, lists:append(NewVbs, Res), IsNotification); {ErrorStatus, ErrorIndex, _} -> @@ -168,6 +177,8 @@ do_get_subagents([], Res, _IsNotification) -> %% #varbind | %% List of #varbind %%----------------------------------------------------------------- + +-dialyzer({nowarn_function, try_get/2}). try_get(IVb, IsNotification) when is_record(IVb, ivarbind) -> ?vtrace("try_get(ivarbind) -> entry with" "~n IVb: ~p", [IVb]), @@ -186,9 +197,12 @@ try_get({TableOid, TableVbs}, IsNotification) -> NVbs ++ NoAccessVbs end. + %%----------------------------------------------------------------- %% Make sure all requested columns are accessible. %%----------------------------------------------------------------- + +-dialyzer({nowarn_function, check_all_table_vbs/4}). check_all_table_vbs([IVb| IVbs], IsNotification, NoA, A) -> #ivarbind{mibentry = Me, varbind = Vb} = IVb, case Me#me.access of @@ -210,6 +224,7 @@ check_all_table_vbs([], _IsNotification, NoA, A) -> {NoA, A}. %% Returns: {error, ErrorStatus, OrgIndex} | %% #varbind %%----------------------------------------------------------------- +-dialyzer({nowarn_function, get_var_value_from_ivb/2}). get_var_value_from_ivb(IVb, IsNotification) when IVb#ivarbind.status =:= noError -> ?vtrace("get_var_value_from_ivb(noError) -> entry", []), @@ -242,6 +257,7 @@ get_var_value_from_ivb(#ivarbind{status = Status, varbind = Vb}, _) -> %%----------------------------------------------------------------- %% Pre: Oid is a correct instance Oid (lookup checked that). %% Returns: A correct return value (see ?AGENT:make_value_a_correct_value) +-dialyzer({nowarn_function, get_var_value_from_mib/2}). get_var_value_from_mib(#me{entrytype = variable, asn1_type = ASN1Type, mfa = {Mod, Func, Args}}, @@ -280,6 +296,7 @@ get_var_value_from_mib(#me{entrytype = table_column, %% non-existing row). %% Returns: {error, ErrorStatus, OrgIndex} | %% {value, Type, Value} +-dialyzer({nowarn_function, get_tab_value_from_mib/3}). get_tab_value_from_mib(#me{mfa = {Mod, Func, Args}}, TableOid, TableVbs) -> ?vtrace("get_tab_value_from_mib -> entry when" "~n Mod: ~p" @@ -302,12 +319,14 @@ get_tab_value_from_mib(#me{mfa = {Mod, Func, Args}}, TableOid, TableVbs) -> %% #varbind. %% The Values list comes from validate_tab_res. %%----------------------------------------------------------------- +-dialyzer({nowarn_function, merge_varbinds_and_value/2}). merge_varbinds_and_value(IVbs, [{{value, Type, Value}, Index} | Values]) -> #ivarbind{varbind = Vb} = lists:nth(Index, IVbs), [Vb#varbind{variabletype = Type, value = Value} | merge_varbinds_and_value(IVbs, Values)]; merge_varbinds_and_value(_, []) -> []. +-dialyzer({nowarn_function, get_value_all_rows/5}). get_value_all_rows([{[], OrgCols} | Rows], Mod, Func, Args, Res) -> ?vtrace("get_value_all_rows -> entry when" "~n OrgCols: ~p", [OrgCols]), @@ -352,10 +371,13 @@ delete_index([]) -> []. %% the retrieved values to reconstruct the original column list, %% but with the retrieved value for each column. %%----------------------------------------------------------------- + +-dialyzer({nowarn_function, remove_duplicates/1}). remove_duplicates(Cols) -> remove_duplicates(Cols, [], []). +-dialyzer({nowarn_function, remove_duplicates/3}). remove_duplicates([{Col, V1, OrgIdx1}, {Col, V2, OrgIdx2} | T], NCols, Dup) -> remove_duplicates([{Col, V1, OrgIdx1} | T], NCols, [{Col, V2, OrgIdx2} | Dup]); @@ -364,6 +386,7 @@ remove_duplicates([Col | T], NCols, Dup) -> remove_duplicates([], NCols, Dup) -> {lists:reverse(NCols), lists:reverse(Dup)}. +-dialyzer({nowarn_function, restore_duplicates/2}). restore_duplicates([], Cols) -> [{Val, OrgIndex} || {_Col, Val, OrgIndex} <- Cols]; restore_duplicates([{Col, _Val2, OrgIndex2} | Dup], @@ -385,6 +408,7 @@ restore_duplicates(Dup, [{_Col, Val, OrgIndex} | T]) -> %% each element in Values and OrgCols correspond to each %% other. %%----------------------------------------------------------------- +-dialyzer({nowarn_function, validate_tab_res/3}). validate_tab_res(Values, OrgCols, Mfa) when is_list(Values) -> {_Col, _ASN1Type, OneIdx} = hd(OrgCols), validate_tab_res(Values, OrgCols, Mfa, [], OneIdx); @@ -407,6 +431,7 @@ validate_tab_res(Error, [{_Col, _ASN1Type, Index} | _OrgCols], Mfa) -> ?LIB:user_err("Invalid return value ~w from ~w (get)",[Error, Mfa]), {error, genErr, Index}. +-dialyzer({nowarn_function, validate_tab_res/5}). validate_tab_res([Value | Values], [{Col, ASN1Type, Index} | OrgCols], Mfa, Res, I) -> diff --git a/lib/snmp/src/agent/snmpa_local_db.erl b/lib/snmp/src/agent/snmpa_local_db.erl index f481641242..c9093fcdb9 100644 --- a/lib/snmp/src/agent/snmpa_local_db.erl +++ b/lib/snmp/src/agent/snmpa_local_db.erl @@ -733,16 +733,16 @@ dets_backup(close, _Cont, _D, B) -> ok; dets_backup(read, Cont1, D, B) -> case dets:bchunk(D, Cont1) of + {error, _} = ERROR -> + ERROR; + '$end_of_table' -> + dets:close(B), + end_of_input; {Cont2, Data} -> F = fun(Arg) -> dets_backup(Arg, Cont2, D, B) end, - {Data, F}; - '$end_of_table' -> - dets:close(B), - end_of_input; - Error -> - Error + {Data, F} end. diff --git a/lib/snmp/src/agent/snmpa_mib_storage.erl b/lib/snmp/src/agent/snmpa_mib_storage.erl index ed0607fb84..d46dab0be0 100644 --- a/lib/snmp/src/agent/snmpa_mib_storage.erl +++ b/lib/snmp/src/agent/snmpa_mib_storage.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2016. All Rights Reserved. +%% Copyright Ericsson AB 2013-2019. 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. @@ -120,7 +120,7 @@ -callback match_object(TabId :: mib_storage_table_id(), Pattern :: ets:match_pattern()) -> - {ok, Recs :: [tuple()]} | {error, Reason :: term()}. + Recs :: [tuple()] | {error, Reason :: term()}. %% --------------------------------------------------------------- @@ -133,7 +133,7 @@ -callback match_delete(TabId :: mib_storage_table_id(), Pattern :: ets:match_pattern()) -> - {ok, Recs :: [tuple()]} | {error, Reason :: term()}. + Recs :: [tuple()] | {error, Reason :: term()}. %% --------------------------------------------------------------- diff --git a/lib/snmp/src/agent/snmpa_mib_storage_dets.erl b/lib/snmp/src/agent/snmpa_mib_storage_dets.erl index 2459b6bc3e..0fcb8083f5 100644 --- a/lib/snmp/src/agent/snmpa_mib_storage_dets.erl +++ b/lib/snmp/src/agent/snmpa_mib_storage_dets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2016. All Rights Reserved. +%% Copyright Ericsson AB 2013-2019. 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. @@ -291,16 +291,16 @@ dets_backup(close, _Cont, _ID, B) -> ok; dets_backup(read, Cont1, ID, B) -> case dets:bchunk(ID, Cont1) of + {error, _} = ERROR -> + ERROR; + '$end_of_table' -> + dets:close(B), + end_of_input; {Cont2, Data} -> F = fun(Arg) -> dets_backup(Arg, Cont2, ID, B) end, - {Data, F}; - '$end_of_table' -> - dets:close(B), - end_of_input; - Error -> - Error + {Data, F} end. diff --git a/lib/snmp/src/agent/snmpa_mib_storage_ets.erl b/lib/snmp/src/agent/snmpa_mib_storage_ets.erl index 68dfa83247..173dac276e 100644 --- a/lib/snmp/src/agent/snmpa_mib_storage_ets.erl +++ b/lib/snmp/src/agent/snmpa_mib_storage_ets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2013-2016. All Rights Reserved. +%% Copyright Ericsson AB 2013-2019. 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. @@ -189,7 +189,8 @@ read(#tab{id = ID}, Key) -> write(#tab{id = ID, rec_name = RecName}, Rec) when (is_tuple(Rec) andalso (element(1, Rec) =:= RecName)) -> ?vtrace("write to table ~p", [ID]), - ets:insert(ID, Rec). + ets:insert(ID, Rec), + ok. %% --------------------------------------------------------------- @@ -213,7 +214,9 @@ delete(#tab{id = ID, file = File}) -> %% --------------------------------------------------------------- delete(#tab{id = ID}, Key) -> ?vtrace("delete from table ~p: ~p", [ID, Key]), - ets:delete(ID, Key). + ets:delete(ID, Key), + ok. + %% --------------------------------------------------------------- diff --git a/lib/snmp/src/agent/snmpa_mpd.erl b/lib/snmp/src/agent/snmpa_mpd.erl index b440d57d03..2ec5dcb5e6 100644 --- a/lib/snmp/src/agent/snmpa_mpd.erl +++ b/lib/snmp/src/agent/snmpa_mpd.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2015. All Rights Reserved. +%% Copyright Ericsson AB 1997-2019. 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. @@ -76,11 +76,9 @@ init(Vsns) -> ?vlog("init -> entry with" "~n Vsns: ~p", [Vsns]), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - ets:insert(snmp_agent_table, {msg_id, random:uniform(2147483647)}), - ets:insert(snmp_agent_table, {req_id, random:uniform(2147483647)}), + ?SNMP_RAND_SEED(), + ets:insert(snmp_agent_table, {msg_id, rand:uniform(2147483647)}), + ets:insert(snmp_agent_table, {req_id, rand:uniform(2147483647)}), init_counters(), init_versions(Vsns, #state{}). diff --git a/lib/snmp/src/agent/snmpa_network_interface.erl b/lib/snmp/src/agent/snmpa_network_interface.erl index 699fbde671..24985c113e 100644 --- a/lib/snmp/src/agent/snmpa_network_interface.erl +++ b/lib/snmp/src/agent/snmpa_network_interface.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -19,28 +19,56 @@ %% -module(snmpa_network_interface). --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{start_link, 4}, - {get_log_type, 1}, - {set_log_type, 2}, - {get_request_limit, 1}, - {set_request_limit, 2}, - {info, 1}, - {verbosity, 2}]; -behaviour_info(_) -> - undefined. - - -%% behaviour_info(callbacks) -> -%% [{start_link, 4}, -%% {send_pdu, 5}, -%% {send_response_pdu, 6}, -%% {discard_pdu, 6}, -%% {send_pdu_req, 6}, -%% {verbosity, 2}, -%% {change_log_type, 2}]; -%% behaviour_info(_) -> -%% undefined. +%% Note that this behaviour is not enough! +%% There is also a set of mandatory messages which the +%% network interface entity must be able to receive and +%% be able to send. See the documentation for more info. + +-callback start_link(Prio, NoteStore, MasterAgent, Opts) -> + {ok, Pid} | {error, Reason} when + Prio :: low | normal | high, % priority_level(), + NoteStore :: pid(), + MasterAgent :: pid(), + Opts :: [Option], + Option :: {verbosity, snmp:verbosity()} | + {versions, [snmp:version()]} | + term(), + Pid :: pid(), + Reason :: term(). + +-callback info(Pid) -> + Info when + Pid :: pid(), + Info :: [{Key, Value}], + Key :: term(), + Value :: term(). + +-callback verbosity(Pid, Verbosity) -> + snmp:void() when + Pid :: pid(), + Verbosity :: snmp:verbosity(). + +-callback get_log_type(Pid) -> + {ok, LogType} | {error, Reason} when + Pid :: pid(), + LogType :: snmp:atl_type(), + Reason :: term(). + +-callback set_log_type(Pid, NewType) -> + {ok, OldType} | {error, Reason} when + Pid :: pid(), + NewType :: snmp:atl_type(), + OldType :: snmp:atl_type(), + Reason :: term(). + +-callback get_request_limit(Pid) -> + {ok, Limit} when + Pid :: pid(), + Limit :: non_neg_integer() | infinity. + +-callback set_request_limit(Pid, NewLimit) -> + {ok, OldLimit} when + Pid :: pid(), + NewLimit :: non_neg_integer() | infinity, + OldLimit :: non_neg_integer() | infinity. diff --git a/lib/snmp/src/agent/snmpa_set.erl b/lib/snmp/src/agent/snmpa_set.erl index 9833d6fdcc..b3a3bf0ab0 100644 --- a/lib/snmp/src/agent/snmpa_set.erl +++ b/lib/snmp/src/agent/snmpa_set.erl @@ -163,10 +163,14 @@ set_phase_two(MyVarbinds, SubagentVarbinds) -> [MyVarbinds, SubagentVarbinds]), case snmpa_set_lib:try_set(MyVarbinds) of {noError, 0} -> + ?vtrace("set phase two: (local) varbinds set ok", []), set_phase_two_subagents(SubagentVarbinds); - {ErrorStatus, Index} -> + {ErrorStatus, ErrorIndex} -> + ?vlog("set phase two: (local) varbinds set failed" + "~n ErrorStatus: ~p" + "~n ErrorIndex: ~p", [ErrorStatus, ErrorIndex]), set_phase_two_undo_subagents(SubagentVarbinds), - {ErrorStatus, Index} + {ErrorStatus, ErrorIndex} end. %%----------------------------------------------------------------- @@ -188,6 +192,7 @@ set_phase_two_subagents([{SubAgentPid, SAVbs} | SubagentVarbinds]) -> {_SAOids, Vbs} = sa_split(SAVbs), case catch snmpa_agent:subagent_set(SubAgentPid, [phase_two, set, Vbs]) of {noError, 0} -> + ?vtrace("set phase two: subagent ~p varbinds set ok", [SubAgentPid]), set_phase_two_subagents(SubagentVarbinds); {'EXIT', Reason} -> user_err("Lost contact with subagent (set)~n~w. Using genErr", @@ -195,10 +200,14 @@ set_phase_two_subagents([{SubAgentPid, SAVbs} | SubagentVarbinds]) -> set_phase_two_undo_subagents(SubagentVarbinds), {genErr, 0}; {ErrorStatus, ErrorIndex} -> + ?vlog("set phase two: subagent ~p varbinds set failed" + "~n ErrorStatus: ~p" + "~n ErrorIndex: ~p", [SubAgentPid, ErrorStatus, ErrorIndex]), set_phase_two_undo_subagents(SubagentVarbinds), {ErrorStatus, ErrorIndex} end; set_phase_two_subagents([]) -> + ?vtrace("set phase two: subagent(s) set ok", []), {noError, 0}. %%----------------------------------------------------------------- diff --git a/lib/snmp/src/agent/snmpa_set_mechanism.erl b/lib/snmp/src/agent/snmpa_set_mechanism.erl index 2f24f38092..4eee7d7257 100644 --- a/lib/snmp/src/agent/snmpa_set_mechanism.erl +++ b/lib/snmp/src/agent/snmpa_set_mechanism.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -19,18 +19,26 @@ %% -module(snmpa_set_mechanism). --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{do_set, 2}, {do_subagent_set, 1}]; -behaviour_info(_) -> - undefined. +%% -export([behaviour_info/1]). +%% behaviour_info(callbacks) -> +%% [{do_set, 2}, {do_subagent_set, 1}]; +%% behaviour_info(_) -> +%% undefined. + %%----------------------------------------------------------------- %% do_set(MibView, UnsortedVarbinds) %%----------------------------------------------------------------- +-callback do_set(MibView, UnsortedVBs) -> + {noError, 0} | {ErrStatus, ErrIndex} when + MibView :: snmp_view_based_acm_mib:mibview(), + UnsortedVBs :: [snmp:varbind()], + ErrStatus :: snmp:error_status(), + ErrIndex :: snmp:error_index(). + + %%----------------------------------------------------------------- %% do_subagent_set(Args) %% @@ -41,3 +49,18 @@ behaviour_info(_) -> %% [phase_two, set, UnsortedVarbinds] %% [phase_two, undo, UnsortedVarbinds] %%----------------------------------------------------------------- + +%% -callback do_subagent_set(Args) -> +%% {noError, 0} | {ErrStatus, ErrIndex} when +%% Args :: [phase_one, UnsortedVBs] | +%% [phase_two, set, UnsortedVBs] | +%% [phase_two, undo, UnsortedVBs], +%% ErrStatus :: snmp:error_status(), +%% ErrIndex :: snmp:error_index(), +%% UnsortedVBs :: [snmp:varbind()]. + +-callback do_subagent_set(Args) -> + {noError, 0} | {ErrStatus, ErrIndex} when + Args :: list(), + ErrStatus :: snmp:error_status(), + ErrIndex :: snmp:error_index(). diff --git a/lib/snmp/src/agent/snmpa_trap.erl b/lib/snmp/src/agent/snmpa_trap.erl index d04b6a206e..119207c76b 100644 --- a/lib/snmp/src/agent/snmpa_trap.erl +++ b/lib/snmp/src/agent/snmpa_trap.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. 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. @@ -364,13 +364,14 @@ send_trap(TrapRec, NotifyName, ContextName, Recv, Vbs, LocalEngineID, LocalEngineID, ExtraInfo, NetIf) end catch - T:E -> - Info = [{args, [TrapRec, NotifyName, ContextName, - Recv, Vbs, LocalEngineID, ExtraInfo, NetIf]}, - {tag, T}, - {err, E}, - {stacktrace, erlang:get_stacktrace()}], - ?vlog("snmpa_trap:send_trap exception: ~p", [Info]), + C:E:S -> + Info = [{args, [TrapRec, NotifyName, ContextName, + Recv, Vbs, LocalEngineID, ExtraInfo, NetIf]}, + {class, C}, + {err, E}, + {stacktrace, S}], + ?vlog("snmpa_trap:send_trap exception: " + "~n ~p", [Info]), {error, {failed_sending_trap, Info}} end. @@ -1114,7 +1115,7 @@ transform_taddrs(TAddrs) -> %% v2 transform_taddr({?snmpUDPDomain, Addr}) -> - transform_taddr(transportDomainIdpIpv4, Addr); + transform_taddr(transportDomainUdpIpv4, Addr); transform_taddr({?transportDomainUdpIpv4, Addr}) -> transform_taddr(transportDomainUdpIpv4, Addr); transform_taddr({?transportDomainUdpIpv6, Addr}) -> diff --git a/lib/snmp/src/agent/snmpa_usm.erl b/lib/snmp/src/agent/snmpa_usm.erl index fb616cd9ef..1debceae98 100644 --- a/lib/snmp/src/agent/snmpa_usm.erl +++ b/lib/snmp/src/agent/snmpa_usm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1999-2015. All Rights Reserved. +%% Copyright Ericsson AB 1999-2019. 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. @@ -646,10 +646,12 @@ get_des_salt() -> ets:insert(snmp_agent_table, {usm_des_salt, 0}), 0; _ -> % it doesn't exist, initialize - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - R = random:uniform(4294967295), + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + R = rand:uniform(4294967295), ets:insert(snmp_agent_table, {usm_des_salt, R}), R end, @@ -679,10 +681,12 @@ get_aes_salt() -> ets:insert(snmp_agent_table, {usm_aes_salt, 0}), 0; _ -> % it doesn't exist, initialize - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - R = random:uniform(36893488147419103231), + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + R = rand:uniform(36893488147419103231), ets:insert(snmp_agent_table, {usm_aes_salt, R}), R end, diff --git a/lib/snmp/src/app/snmp.erl b/lib/snmp/src/app/snmp.erl index 216452afdd..1e6a93deff 100644 --- a/lib/snmp/src/app/snmp.erl +++ b/lib/snmp/src/app/snmp.erl @@ -95,6 +95,9 @@ dir/0, snmp_timer/0, + atl_type/0, + verbosity/0, + engine_id/0, tdomain/0, community/0, @@ -188,6 +191,9 @@ -type dir() :: string(). -type snmp_timer() :: #snmp_incr_timer{}. +-type atl_type() :: read | write | read_write. +-type verbosity() :: info | log | debug | trace | silence. + -type engine_id() :: string(). -type tdomain() :: transportDomainUdpIpv4 | transportDomainUdpIpv6. -type community() :: string(). @@ -590,15 +596,6 @@ print_mod_info(Prefix, {Module, Info}) -> _ -> "Not found" end, - CompDate = - case key1search(compile_time, Info) of - {value, {Year, Month, Day, Hour, Min, Sec}} -> - io_lib:format( - "~w-~2..0w-~2..0w ~2..0w:~2..0w:~2..0w", - [Year, Month, Day, Hour, Min, Sec]); - _ -> - "Not found" - end, Digest = case key1search(md5, Info) of {value, MD5} when is_binary(MD5) -> @@ -610,13 +607,11 @@ print_mod_info(Prefix, {Module, Info}) -> "~s Vsn: ~s~n" "~s App vsn: ~s~n" "~s Compiler ver: ~s~n" - "~s Compile time: ~s~n" "~s MD5 digest: ~s~n", [Prefix, Module, Prefix, Vsn, Prefix, AppVsn, Prefix, CompVer, - Prefix, CompDate, Prefix, Digest]), ok. @@ -711,13 +706,8 @@ sys_info() -> [{arch, SysArch}, {ver, SysVer}]. os_info() -> - V = os:version(), - case os:type() of - {OsFam, OsName} -> - [{fam, OsFam}, {name, OsName}, {ver, V}]; - OsFam -> - [{fam, OsFam}, {ver, V}] - end. + {OsFam, OsName} = os:type(), + [{fam, OsFam}, {name, OsName}, {ver, os:version()}]. ms1() -> App = ?APPLICATION, diff --git a/lib/snmp/src/app/snmp_internal.hrl b/lib/snmp/src/app/snmp_internal.hrl index 374767df15..f9a758ab7b 100644 --- a/lib/snmp/src/app/snmp_internal.hrl +++ b/lib/snmp/src/app/snmp_internal.hrl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2016. All Rights Reserved. +%% Copyright Ericsson AB 2006-2019. 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. @@ -25,7 +25,12 @@ -define(APPLICATION, snmp). -endif. --define(STACK(), erlang:get_stacktrace()). + +-define(SNMP_RAND_SEED_ALG, exrop). +-define(SNMP_RAND_SEED(), rand:seed(?SNMP_RAND_SEED_ALG, + {erlang:phash2([node()]), + erlang:monotonic_time(), + erlang:unique_integer()})). -define(snmp_info(C, F, A), ?snmp_msg(info_msg, C, F, A)). -define(snmp_warning(C, F, A), ?snmp_msg(warning_msg, C, F, A)). @@ -39,5 +44,3 @@ -endif. % -ifdef(snmp_internal). - - diff --git a/lib/snmp/src/compile/Makefile b/lib/snmp/src/compile/Makefile index 4093ffa9ca..d9678669a5 100644 --- a/lib/snmp/src/compile/Makefile +++ b/lib/snmp/src/compile/Makefile @@ -2,7 +2,7 @@ # %CopyrightBegin% # -# Copyright Ericsson AB 1997-2016. All Rights Reserved. +# Copyright Ericsson AB 1997-2019. 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. @@ -59,6 +59,8 @@ PARSER_TARGET = $(PARSER_MODULE).$(EMULATOR) # ---------------------------------------------------- # FLAGS # ---------------------------------------------------- +ERL_COMPILE_FLAGS += -pa $(ERL_TOP)/lib/snmp/ebin + ifeq ($(WARN_UNUSED_VARS),true) ERL_COMPILE_FLAGS += +warn_unused_vars endif diff --git a/lib/snmp/src/compile/snmpc.erl b/lib/snmp/src/compile/snmpc.erl index c810bfcd41..4249799195 100644 --- a/lib/snmp/src/compile/snmpc.erl +++ b/lib/snmp/src/compile/snmpc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2018. All Rights Reserved. +%% Copyright Ericsson AB 1997-2019. 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. @@ -31,6 +31,7 @@ -export([init/3]). -include_lib("stdlib/include/erl_compile.hrl"). +-include_lib("snmp/src/app/snmp_internal.hrl"). -include("snmp_types.hrl"). -include("snmpc.hrl"). -include("snmpc_lib.hrl"). @@ -413,9 +414,11 @@ get_verbosity(Options) -> %%---------------------------------------------------------------------- init(From, MibFileName, Options) -> - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), put(options, Options), put(verbosity, get_verbosity(Options)), put(description, get_description(Options)), diff --git a/lib/snmp/src/manager/snmpm.erl b/lib/snmp/src/manager/snmpm.erl index cf8c95d69f..8e60cecaf9 100644 --- a/lib/snmp/src/manager/snmpm.erl +++ b/lib/snmp/src/manager/snmpm.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -32,7 +32,7 @@ %% Management API start/0, start/1, start_link/0, start_link/1, - stop/0, + stop/0, stop/1, monitor/0, demonitor/1, notify_started/1, cancel_notify_started/1, @@ -196,7 +196,12 @@ start(Opts) -> ok. stop() -> - snmpm_supervisor:stop(). + stop(0). + +stop(Timeout) when (Timeout =:= infinity) orelse + (is_integer(Timeout) andalso (Timeout >= 0)) -> + snmpm_supervisor:stop(Timeout). + monitor() -> diff --git a/lib/snmp/src/manager/snmpm_conf.erl b/lib/snmp/src/manager/snmpm_conf.erl index 0421f49c88..d097b40438 100644 --- a/lib/snmp/src/manager/snmpm_conf.erl +++ b/lib/snmp/src/manager/snmpm_conf.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2006-2016. All Rights Reserved. +%% Copyright Ericsson AB 2006-2019. 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. @@ -356,6 +356,7 @@ read_config_file(Dir, File, Order, Check) -> %% ---- config file utility functions ---- +-dialyzer({nowarn_function, check_ok/1}). % Future compat check_ok(ok) -> ok; check_ok({ok, _}) -> diff --git a/lib/snmp/src/manager/snmpm_config.erl b/lib/snmp/src/manager/snmpm_config.erl index 118cdcd1df..4653d9822d 100644 --- a/lib/snmp/src/manager/snmpm_config.erl +++ b/lib/snmp/src/manager/snmpm_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -479,10 +479,7 @@ agent_info(Domain, Address, Item) when is_atom(Domain) -> NAddress -> do_agent_info(Domain, NAddress, Item) catch - _Thrown -> - %% p(?MODULE_STRING":agent_info(~p, ~p, ~p) throwed ~p at.~n" - %% " ~p", - %% [Domain, Address, Item, _Thrown, erlang:get_stacktrace()]), + _C:_E:_S -> {error, not_found} end; agent_info(Ip, Port, Item) when is_integer(Port) -> @@ -493,10 +490,7 @@ agent_info(Ip, Port, Item) when is_integer(Port) -> Address -> do_agent_info(Domain, Address, Item) catch - _Thrown -> - %% p(?MODULE_STRING":agent_info(~p, ~p, ~p) throwed ~p at.~n" - %% " ~p", - %% [Ip, Port, Item, _Thrown, erlang:get_stacktrace()]), + _C:_E:_S -> {error, not_found} end. @@ -1688,9 +1682,10 @@ read_agents_config_file(Dir) -> Check = fun check_agent_config/2, try read_file(Dir, "agents.conf", Order, Check, []) catch - throw:Error -> - ?vlog("agent config error: ~p", [Error]), - erlang:raise(throw, Error, erlang:get_stacktrace()) + throw:E:S -> + ?vlog("agent config error: " + "~n ~p", [E]), + erlang:raise(throw, E, S) end. check_agent_config(Agent, State) -> @@ -1935,9 +1930,10 @@ read_users_config_file(Dir) -> Check = fun (User, State) -> {check_user_config(User), State} end, try read_file(Dir, "users.conf", Order, Check, []) catch - throw:Error -> - ?vlog("failure reading users config file: ~n ~p", [Error]), - erlang:raise(throw, Error, erlang:get_stacktrace()) + throw:E:S -> + ?vlog("failure reading users config file: " + "~n ~p", [E]), + erlang:raise(throw, E, S) end. check_user_config({Id, Mod, Data}) -> @@ -2351,10 +2347,11 @@ read_file(Dir, FileName, Order, Check, Default) -> read_file(Dir, FileName, Order, Check) -> try snmp_conf:read(filename:join(Dir, FileName), Order, Check) catch - throw:{error, Reason} = Error + throw:{error, Reason} = E:S when element(1, Reason) =:= failed_open -> - error_msg("failed reading config from ~s: ~p", [FileName, Reason]), - erlang:raise(throw, Error, erlang:get_stacktrace()) + error_msg("failed reading config from ~s: " + "~n ~p", [FileName, Reason]), + erlang:raise(throw, E, S) end. %%-------------------------------------------------------------------- @@ -2741,16 +2738,16 @@ dets_backup(close, _Cont, _D, B) -> ok; dets_backup(read, Cont1, D, B) -> case dets:bchunk(D, Cont1) of + {error, _} = ERROR -> + ERROR; + '$end_of_table' -> + dets:close(B), + end_of_input; {Cont2, Data} -> F = fun(Arg) -> dets_backup(Arg, Cont2, D, B) end, - {Data, F}; - '$end_of_table' -> - dets:close(B), - end_of_input; - Error -> - Error + {Data, F} end. diff --git a/lib/snmp/src/manager/snmpm_mpd.erl b/lib/snmp/src/manager/snmpm_mpd.erl index 191dc2c281..8d0a7918a6 100644 --- a/lib/snmp/src/manager/snmpm_mpd.erl +++ b/lib/snmp/src/manager/snmpm_mpd.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2015. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -68,11 +68,13 @@ %%%----------------------------------------------------------------- init(Vsns) -> ?vdebug("init -> entry with ~p", [Vsns]), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), - snmpm_config:cre_counter(msg_id, random:uniform(2147483647)), - snmpm_config:cre_counter(req_id, random:uniform(2147483647)), + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), + snmpm_config:cre_counter(msg_id, rand:uniform(2147483647)), + snmpm_config:cre_counter(req_id, rand:uniform(2147483647)), init_counters(), State = init_versions(Vsns, #state{}), init_usm(State#state.v3), diff --git a/lib/snmp/src/manager/snmpm_net_if.erl b/lib/snmp/src/manager/snmpm_net_if.erl index 29216f9d6a..184f782860 100644 --- a/lib/snmp/src/manager/snmpm_net_if.erl +++ b/lib/snmp/src/manager/snmpm_net_if.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2017. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -182,11 +182,9 @@ worker(Worker, Failer, #state{log = Log} = State) -> %% Winds up in handle_info {'DOWN', ...} erlang:exit({net_if_worker, Result}) catch - Class:Reason -> + C:E:S -> %% Winds up in handle_info {'DOWN', ...} - erlang:exit( - {net_if_worker, Failer, - Class, Reason, erlang:get_stacktrace()}) + erlang:exit({net_if_worker, Failer, C, E, S}) end end, [monitor]). @@ -983,11 +981,10 @@ udp_send(Sock, To, Msg) -> error_msg("failed sending message to ~p:~p:~n" " ~p",[IpAddr, IpPort, Reason]) catch - error:Error -> - error_msg("failed sending message to ~p:~p:~n" - " error:~p~n" - " ~p", - [IpAddr, IpPort, Error, erlang:get_stacktrace()]) + error:E:S -> + error_msg("failed sending message to ~p:~p:" + "~n ~p" + "~n ~p", [IpAddr, IpPort, E, S]) end. sz(B) when is_binary(B) -> diff --git a/lib/snmp/src/manager/snmpm_network_interface.erl b/lib/snmp/src/manager/snmpm_network_interface.erl index d0f6f709d3..7123bb94f0 100644 --- a/lib/snmp/src/manager/snmpm_network_interface.erl +++ b/lib/snmp/src/manager/snmpm_network_interface.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -20,19 +20,61 @@ -module(snmpm_network_interface). --export([behaviour_info/1]). - -behaviour_info(callbacks) -> - [{start_link, 2}, - {stop, 1}, - {send_pdu, 7}, - {inform_response, 4}, - {note_store, 2}, - {info, 1}, - {verbosity, 2}, - %% {system_info_updated, 2}, - {get_log_type, 1}, - {set_log_type, 2}]; -behaviour_info(_) -> - undefined. +-callback start_link(Server, NoteStore) -> + {ok, Pid} | {error, Reason} when + Server :: pid(), + NoteStore :: pid(), + Pid :: pid(), + Reason :: term(). + +-callback stop(Pid) -> + snmp:void() when + Pid :: pid(). + +-callback send_pdu(Pid, Pdu, Vsn, MsgData, Domain, Addr, ExtraInfo) -> + snmp:void() when + Pid :: pid(), + Pdu :: snmp:pdu(), + Vsn :: 'version-1' | 'version-2' | 'version-3', + MsgData :: term(), + Domain :: snmp:tdomain(), + Addr :: {inet:ip_address(), inet:port_number()}, + ExtraInfo :: term(). + +-callback inform_response(Pid, Ref, Addr, Port) -> + snmp:void() when + Pid :: pid(), + Ref :: term(), + Addr :: inet:ip_address(), + Port :: inet:port_number(). + +-callback note_store(Pid, NoteStore) -> + snmp:void() when + Pid :: pid(), + NoteStore :: pid(). + +-callback info(Pid) -> + Info when + Pid :: pid(), + Info :: [{Key, Value}], + Key :: term(), + Value :: term(). + +-callback verbosity(Pid, Verbosity) -> + snmp:void() when + Pid :: pid(), + Verbosity :: snmp:verbosity(). + +-callback get_log_type(Pid) -> + {ok, LogType} | {error, Reason} when + Pid :: pid(), + LogType :: snmp:atl_type(), + Reason :: term(). + +-callback set_log_type(Pid, NewType) -> + {ok, OldType} | {error, Reason} when + Pid :: pid(), + NewType :: snmp:atl_type(), + OldType :: snmp:atl_type(), + Reason :: term(). diff --git a/lib/snmp/src/manager/snmpm_server.erl b/lib/snmp/src/manager/snmpm_server.erl index c8d7fa1e8b..a6ca2b2b14 100644 --- a/lib/snmp/src/manager/snmpm_server.erl +++ b/lib/snmp/src/manager/snmpm_server.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2015. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -1755,9 +1755,10 @@ handle_error(_UserId, Mod, Reason, ReqId, Data, _State) -> Mod:handle_error(ReqId, Reason, Data) end catch - T:E -> + C:E:S -> CallbackArgs = [ReqId, Reason, Data], - handle_invalid_result(handle_error, CallbackArgs, T, E) + handle_invalid_result(handle_error, CallbackArgs, + C, E, S) end end, handle_callback(F), @@ -1948,9 +1949,10 @@ handle_pdu( Mod:handle_pdu(TargetName, ReqId, SnmpResponse, Data) end catch - T:E -> + C:E:S -> CallbackArgs = [TargetName, ReqId, SnmpResponse, Data], - handle_invalid_result(handle_pdu, CallbackArgs, T, E) + handle_invalid_result(handle_pdu, CallbackArgs, + C, E, S) end end, handle_callback(F), @@ -2119,10 +2121,10 @@ do_handle_agent(DefUserId, DefMod, "<~p,~p>: ~n~w", [Type, Domain, Addr, SnmpInfo]) end; - T:E -> + C:E:S -> CallbackArgs = [Domain_or_Ip, Addr_or_Port, Type, SnmpInfo, DefData], - handle_invalid_result(handle_agent, CallbackArgs, T, E) + handle_invalid_result(handle_agent, CallbackArgs, C, E, S) end. @@ -2331,8 +2333,8 @@ do_handle_trap( handle_invalid_result(handle_trap, CallbackArgs, InvalidResult) catch - T:E -> - handle_invalid_result(handle_trap, CallbackArgs, T, E) + C:E:S -> + handle_invalid_result(handle_trap, CallbackArgs, C, E, S) end. @@ -2523,8 +2525,8 @@ do_handle_inform( reply catch - T:E -> - handle_invalid_result(handle_inform, CallbackArgs, T, E), + C:E:S -> + handle_invalid_result(handle_inform, CallbackArgs, C, E, S), reply end, @@ -2837,8 +2839,8 @@ do_handle_report( reply catch - T:E -> - handle_invalid_result(handle_report, CallbackArgs, T, E), + C:E:S -> + handle_invalid_result(handle_report, CallbackArgs, C, E, S), reply end. @@ -2855,15 +2857,14 @@ handle_callback(F) -> -handle_invalid_result(Func, Args, T, E) -> - Stacktrace = ?STACK(), +handle_invalid_result(Func, Args, C, E, S) -> error_msg("Callback function failed: " "~n Function: ~p" "~n Args: ~p" - "~n Error Type: ~p" + "~n Class: ~p" "~n Error: ~p" "~n Stacktrace: ~p", - [Func, Args, T, E, Stacktrace]). + [Func, Args, C, E, S]). handle_invalid_result(Func, Args, InvalidResult) -> error_msg("Callback function returned invalid result: " diff --git a/lib/snmp/src/manager/snmpm_supervisor.erl b/lib/snmp/src/manager/snmpm_supervisor.erl index c36bbe1bdd..0061488f54 100644 --- a/lib/snmp/src/manager/snmpm_supervisor.erl +++ b/lib/snmp/src/manager/snmpm_supervisor.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -24,7 +24,7 @@ %% External exports --export([start_link/2, stop/0]). +-export([start_link/2, stop/0, stop/1]). %% supervisor callbacks -export([init/1]). @@ -44,20 +44,47 @@ start_link(Type, Opts) -> SupName = {local, ?MODULE}, supervisor:start_link(SupName, ?MODULE, [Type, Opts]). + stop() -> + stop(0). + +stop(Timeout) -> ?d("stop -> entry", []), case whereis(?SERVER) of Pid when is_pid(Pid) -> - ?d("stop -> Pid: ~p", [Pid]), - exit(Pid, shutdown), - ?d("stop -> stopped", []), - ok; + stop(Pid, Timeout); _ -> ?d("stop -> not running", []), not_running end. +%% For some unfathomable reason there is no "nice" way to stop +%% a supervisor. The "normal" way to do it is: +%% 1) exit(Pid, kill) (kaboom) +%% 2) If the caller is the *parent*: exit(Pid, shutdown) +%% So, here we do it the really unly way...but since this function is +%% intended for testing (mostly)... +stop(Pid, Timeout) when (Timeout =:= 0) -> + ?d("stop -> Pid: ~p", [Pid]), + sys:terminate(whereis(?SERVER), shutdown), + ?d("stop -> stopped", []), + ok; +stop(Pid, Timeout) -> + ?d("stop -> Pid: ~p", [Pid]), + MRef = erlang:monitor(process, Pid), + sys:terminate(whereis(?SERVER), shutdown), + receive + {'DOWN', MRef, process, Pid, _} -> + ?d("stop -> stopped", []), + ok + after Timeout -> + ?d("stop -> timeout", []), + erlang:demonitor(MRef, [flush]), + {error, timeout} + end. + + %%%------------------------------------------------------------------- %%% Callback functions from supervisor %%%------------------------------------------------------------------- diff --git a/lib/snmp/src/misc/snmp_conf.erl b/lib/snmp/src/misc/snmp_conf.erl index 513616a285..20b7af0373 100644 --- a/lib/snmp/src/misc/snmp_conf.erl +++ b/lib/snmp/src/misc/snmp_conf.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. 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. @@ -164,6 +164,14 @@ no_filter(X) -> X. %% An ordering function (A, B) shall return true iff %% A is less than or equal to B i.e shall return %% false iff A is to be ordered after B. + +-spec keyorder(N, A, B, Keys) -> + boolean() when + N :: integer(), + A :: tuple(), + B :: tuple(), + Keys :: maybe_improper_list(). + keyorder(N, A, B, _) when element(N, A) == element(N, B) -> true; keyorder(N, A, B, [Key | _]) @@ -236,15 +244,16 @@ read_check(File, Check, [{StartLine, Row, EndLine}|Lines], State, Res) -> " NewRow: ~p~n", [NewRow]), read_check(File, Check, Lines, NewState, [NewRow | Res]) catch - {error, Reason} -> - ?vtrace("read_check -> error:~n" - " Reason: ~p", [Reason]), + throw:{error, Reason} -> + ?vtrace("read_check -> error:" + "~n Reason: ~p", [Reason]), error({failed_check, File, StartLine, EndLine, Reason}); - Class:Reason -> - Error = {Class,Reason,erlang:get_stacktrace()}, - ?vtrace("read_check -> failure:~n" - " Error: ~p", [Error]), - error({failed_check, File, StartLine, EndLine, Error}) + C:E:S -> + ?vtrace("read_check -> failure:" + "~n Class: ~p" + "~n Error: ~p" + "~n Stack: ~p", [C, E, S]), + error({failed_check, File, StartLine, EndLine, {C, E, S}}) end. open_file(File) -> diff --git a/lib/snmp/src/misc/snmp_config.erl b/lib/snmp/src/misc/snmp_config.erl index 45661b71a7..3104f2a096 100644 --- a/lib/snmp/src/misc/snmp_config.erl +++ b/lib/snmp/src/misc/snmp_config.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2016. All Rights Reserved. +%% Copyright Ericsson AB 1996-2019. 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. @@ -96,17 +96,15 @@ ]). --export_type([void/0, - order_config_entry_function/0, +-export_type([ + order_config_entry_function/0, check_config_entry_function/0, - write_config_function/0]). + write_config_function/0 + ]). %%---------------------------------------------------------------------- --type void() :: term(). % Any value - ignored - - %%---------------------------------------------------------------------- %% Handy SNMP configuration %%---------------------------------------------------------------------- @@ -1106,6 +1104,7 @@ verify_sec_type(ST) -> {error, "invalid security type: " ++ ST}. verify_address(A) -> verify_address(A, snmpUDPDomain). +-dialyzer({nowarn_function, verify_address/2}). % Future compat verify_address(A, snmpUDPDomain = _Domain) -> do_verify_address(A, inet); verify_address(A, transportDomainUdpIpv4 = _Domain) -> @@ -2573,15 +2572,17 @@ write_config_file(Dir, FileName, Order, Check, Write, Entries) Error end catch - Error -> - S = erlang:get_stacktrace(), - d("File write of ~s throwed: ~p~n ~p~n", - [FileName, Error, S]), - Error; - C:E -> - S = erlang:get_stacktrace(), - d("File write of ~s exception: ~p:~p~n ~p~n", - [FileName,C,E,S]), + throw:E:S -> + d("File write of ~s throwed: " + "~n ~p" + "~n ~p" + "~n", [FileName, E, S]), + E; + C:E:S -> + d("File write of ~s exception: " + "~n ~p:~p" + "~n ~p" + "~n", [FileName, C, E, S]), {error, {failed_write, Dir, FileName, {C, E, S}}} end. @@ -2590,16 +2591,18 @@ write_config_file(Dir, FileName, Write, Entries, Fd) -> ok -> close_config_file(Dir, FileName, Fd) catch - Error -> - S = erlang:get_stacktrace(), - d("File write of ~s throwed: ~p~n ~p~n", - [FileName, Error, S]), + throw:E:S -> + d("File write of ~s throwed: " + "~n ~p" + "~n ~p" + "~n", [FileName, E, S]), close_config_file(Dir, FileName, Fd), - Error; - C:E -> - S = erlang:get_stacktrace(), - d("File write of ~s exception: ~p:~p~n ~p~n", - [FileName,C,E,S]), + E; + C:E:S -> + d("File write of ~s exception: " + "~n ~p:~p" + "~n ~p" + "~n", [FileName, C, E, S]), close_config_file(Dir, FileName, Fd), {error, {failed_write, Dir, FileName, {C, E, S}}} end. @@ -2661,16 +2664,18 @@ append_config_file(Dir, FileName, Order, Check, Write, Entries, Fd) -> ok -> close_config_file(Dir, FileName, Fd) catch - Error -> - S = erlang:get_stacktrace(), - d("File append of ~s throwed: ~p~n ~p~n", - [FileName, Error, S]), + throw:E:S -> + d("File append of ~s throwed: " + "~n ~p" + "~n ~p" + "~n", [FileName, E, S]), close_config_file(Dir, FileName, Fd), - Error; - C:E -> - S = erlang:get_stacktrace(), - d("File append of ~s exception: ~p:~p~n ~p~n", - [FileName,C,E,S]), + E; + C:E:S -> + d("File append of ~s exception: " + "~n ~p:~p" + "~n ~p" + "~n", [FileName, C, E, S]), close_config_file(Dir, FileName, Fd), {error, {failed_append, Dir, FileName, {C, E, S}}} end. @@ -2702,16 +2707,18 @@ read_config_file(Dir, FileName, Order, Check) SortedLines = sort_lines(Lines, Order), {ok, verify_lines(SortedLines, Check, undefined, [])} catch - Error -> - S = erlang:get_stacktrace(), - d("File read of ~s throwed: ~p~n ~p~n", - [FileName, Error, S]), - {error, Error}; - T:E -> - S = erlang:get_stacktrace(), - d("File read of ~s exception: ~p:~p~n ~p~n", - [FileName,T,E,S]), - {error, {failed_read, Dir, FileName, {T, E, S}}} + throw:E:S -> + d("File read of ~s throwed: " + "~n ~p" + "~n ~p" + "~n", [FileName, E, S]), + {error, E}; + C:E:S -> + d("File read of ~s exception: " + "~n ~p:~p" + "~n ~p" + "~n", [FileName, C, E, S]), + {error, {failed_read, Dir, FileName, {C, E, S}}} after file:close(Fd) end; @@ -2760,11 +2767,10 @@ verify_lines( {{ok, NewTerm}, NewState} -> verify_lines(Lines, Check, NewState, [NewTerm|Acc]) catch - {error, Reason} -> + throw:{error, Reason}:_ -> throw({failed_check, StartLine, EndLine, Reason}); - C:R -> - S = erlang:get_stacktrace(), - throw({failed_check, StartLine, EndLine, {C, R, S}}) + C:E:S -> + throw({failed_check, StartLine, EndLine, {C, E, S}}) end. diff --git a/lib/snmp/src/misc/snmp_log.erl b/lib/snmp/src/misc/snmp_log.erl index 5713c14912..8a4dfa621b 100644 --- a/lib/snmp/src/misc/snmp_log.erl +++ b/lib/snmp/src/misc/snmp_log.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2018. All Rights Reserved. +%% Copyright Ericsson AB 1997-2019. 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. @@ -342,9 +342,9 @@ validate_loop({Cont, Terms, BadBytes}, Log, Validator, PrevTS, PrevSN) -> ?vtrace("validate_loop -> " "~n NextTS: ~p" "~n NextSN: ~p", [NextTS, NextSN]), - validate_loop(disk_log:chunk(Log, Cont), Log, Validator, NextTS, NextSN); -validate_loop(Error, _Log, _Write, _PrevTS, _PrevSN) -> - Error. + validate_loop(disk_log:chunk(Log, Cont), Log, Validator, NextTS, NextSN). +%% validate_loop(Error, _Log, _Write, _PrevTS, _PrevSN) -> +%% Error. %% -- log --- @@ -924,14 +924,7 @@ f(TimeStamp, SeqNo, end, format_tab( "~w ~s - ~s [~s]~s ~w\n~s", - [Class, AddrStr, HdrStr, TimeStamp, SeqNo, Vsn, Str]); -f(TimeStamp, SeqNo, Msg, AddrStr, _Mib) -> - io:format("<ERROR> Unexpected data: " - "~n TimeStamp: ~s~s" - "~n Msg: ~p" - "~n AddrStr: ~p" - "~n", [TimeStamp, SeqNo, Msg, AddrStr]), - throw({error, 'invalid-message'}). + [Class, AddrStr, HdrStr, TimeStamp, SeqNo, Vsn, Str]). f(F, A) -> lists:flatten(io_lib:format(F, A)). diff --git a/lib/snmp/test/Makefile b/lib/snmp/test/Makefile index a9142d911d..d9b01536ea 100644 --- a/lib/snmp/test/Makefile +++ b/lib/snmp/test/Makefile @@ -180,7 +180,7 @@ emakebuild: $(EMAKEFILE) targets: mib $(EMAKEFILE) erl -make -old_targets: $(TARGET_FILES) $(TEST_SERVER_TARGETS) +old_targets: mib $(TARGET_FILES) $(TEST_SERVER_TARGETS) $(EMAKEFILE): Makefile $(MAKE_EMAKE) $(ERL_COMPILE_FLAGS) -o$(EBIN) '*_SUITE_make' > $(EMAKEFILE) diff --git a/lib/snmp/test/modules.mk b/lib/snmp/test/modules.mk index 8b6547f9a9..ec3870dbd8 100644 --- a/lib/snmp/test/modules.mk +++ b/lib/snmp/test/modules.mk @@ -42,6 +42,8 @@ SUITE_MODULES = \ snmp_manager_test TEST_UTIL_MODULES = \ + snmp_test_global_sys_monitor \ + snmp_test_sys_monitor \ snmp_test_lib \ snmp_test_manager \ snmp_test_mgr \ diff --git a/lib/snmp/test/snmp_agent_test.erl b/lib/snmp/test/snmp_agent_test.erl index 71e3fa3b9a..a45cfa9e98 100644 --- a/lib/snmp/test/snmp_agent_test.erl +++ b/lib/snmp/test/snmp_agent_test.erl @@ -557,6 +557,8 @@ init_per_suite(Config0) when is_list(Config0) -> Config3 = [{mib_dir, MibDir}, {std_mib_dir, StdMibDir} | Config2], + snmp_test_global_sys_monitor:start(), + snmp_test_sys_monitor:start(), % We need one on this node also snmp_test_mgr_counter_server:start(), p("init_per_suite -> end when" @@ -580,6 +582,8 @@ end_per_suite(Config) when is_list(Config) -> p("end_per_suite -> failed stopping counter server" "~n Reason: ~p", [Reason]) end, + snmp_test_sys_monitor:stop(), + snmp_test_global_sys_monitor:stop(), p("end_per_suite -> end when" "~n Nodes: ~p", [erlang:nodes()]), @@ -667,22 +671,39 @@ init_per_group(GroupName, Config) -> snmp_test_lib:init_group_top_dir(GroupName, Config). init_per_group_ipv6(GroupName, Config, Init) -> - {ok, Hostname0} = inet:gethostname(), - case ct:require(ipv6_hosts) of - ok -> - case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of - true -> - Init( - snmp_test_lib:init_group_top_dir( - GroupName, - [{ipfamily, inet6}, - {ip, ?LOCALHOST(inet6)} - | lists:keydelete(ip, 1, Config)])); - false -> - {skip, "Host does not support IPV6"} - end; - _ -> - {skip, "Test config ipv6_hosts is missing"} + %% <OS-CONDITIONAL-SKIP> + %% This is a higly questionable test. + %% But until we have time to figure out what IPv6 issues + %% are actually causing the failures... + OSSkipable = [{unix, + [ + {darwin, fun(V) when (V > {9, 8, 0}) -> + %% This version is OK: No Skip + false; + (_) -> + %% This version is *not* ok: Skip + true + end} + ] + }], + %% </OS-CONDITIONAL-SKIP> + case ?OS_BASED_SKIP(OSSkipable) of + true -> + {skip, "Host *may* not *properly* support IPV6"}; + false -> + %% Even if this host supports IPv6 we don't use it unless its + %% one of the configured/supported IPv6 hosts... + case (?HAS_SUPPORT_IPV6() andalso ?IS_IPV6_HOST()) of + true -> + Init( + snmp_test_lib:init_group_top_dir( + GroupName, + [{ipfamily, inet6}, + {ip, ?LOCALHOST(inet6)} + | lists:keydelete(ip, 1, Config)])); + false -> + {skip, "Host does not support IPv6"} + end end. end_per_group(all_tcs, Config) -> @@ -751,6 +772,8 @@ init_per_testcase(Case, Config) when is_list(Config) -> Result = init_per_testcase1(Case, Config), + snmp_test_global_sys_monitor:reset_events(), + p("init_per_testcase -> done when" "~n Result: ~p" "~n Nodes: ~p", [Result, erlang:nodes()]), @@ -800,6 +823,9 @@ end_per_testcase(Case, Config) when is_list(Config) -> "~n Nodes: ~p", [Config, erlang:nodes()]), display_log(Config), + + p("system events during test: " + "~n ~p", [snmp_test_global_sys_monitor:events()]), Result = end_per_testcase1(Case, Config), @@ -1637,7 +1663,7 @@ create_local_db_dir(Config) when is_list(Config) -> Name = list_to_atom(atom_to_list(create_local_db_dir) ++"-"++As++"-"++Bs++"-"++Cs), Pa = filename:dirname(code:which(?MODULE)), - {ok,Node} = ?t:start_node(Name, slave, [{args, "-pa "++Pa}]), + {ok,Node} = ?t:start_node(Name, slave, [{args, "-pa " ++ Pa}]), %% first start with a nonexisting DbDir Fun1 = fun() -> @@ -6567,7 +6593,6 @@ otp_4394_test() -> gn([[1,1]]), Res = case snmp_test_mgr:expect(1, [{[sysDescr,0], "Erlang SNMP agent"}]) of - %% {error, 1, {"?",[]}, {"~w",[timeout]}} {error, 1, _, {_, [timeout]}} -> ?DBG("otp_4394_test -> expected result: timeout", []), ok; diff --git a/lib/snmp/test/snmp_agent_test_lib.erl b/lib/snmp/test/snmp_agent_test_lib.erl index 6defdadb5a..c0da47dc4c 100644 --- a/lib/snmp/test/snmp_agent_test_lib.erl +++ b/lib/snmp/test/snmp_agent_test_lib.erl @@ -66,7 +66,7 @@ ]). %% Internal exports --export([wait/5, run/4]). +-export([tc_wait/5, tc_run/4]). -include_lib("kernel/include/file.hrl"). -include_lib("common_test/include/ct.hrl"). @@ -276,87 +276,197 @@ init_case(Config) when is_list(Config) -> %%% configuration. %%%-------------------------------------------------- -try_test(Mod, Func) -> - call(get(mgr_node), ?MODULE, run, [Mod, Func, [], []]). - -try_test(Mod, Func, A) -> - call(get(mgr_node), ?MODULE, run, [Mod, Func, A, []]). - -try_test(Mod, Func, A, Opts) -> - call(get(mgr_node), ?MODULE, run, [Mod, Func, A, Opts]). - -call(N,M,F,A) -> - ?DBG("call -> entry with~n" - " N: ~p~n" - " M: ~p~n" - " F: ~p~n" - " A: ~p~n" - " when~n" - " get(): ~p", - [N,M,F,A,get()]), - spawn(N, ?MODULE, wait, [self(),get(),M,F,A]), +try_test(TcRunMod, TcRunFunc) -> + try_test(TcRunMod, TcRunFunc, []). + +try_test(TcRunMod, TcRunFunc, TcRunArgs) -> + try_test(TcRunMod, TcRunFunc, TcRunArgs, []). + +try_test(TcRunMod, TcRunFunc, TcRunArgs, TcRunOpts) -> + Node = get(mgr_node), + Mod = ?MODULE, + Func = tc_run, + Args = [TcRunMod, TcRunFunc, TcRunArgs, TcRunOpts], + tc_try(Node, Mod, Func, Args). + +%% We spawn a test case runner process on the manager node. +%% The assumption is that the manager shall do something, but +%% not all test cases have the manager perform actions. +%% In some cases we make a rpc call back to the agent node directly +%% and call something in the agent... (for example the info_test +%% test case). +%% We should use link (instead of monitor) in order for the test case +%% timeout cleanup (kills) should have effect on the test case runner +%% process as well. + +tc_try(N, M, F, A) -> + ?PRINT2("tc_try -> entry with" + "~n N: ~p" + "~n M: ~p" + "~n F: ~p" + "~n A: ~p" + "~n when" + "~n get(): ~p" + "~n", [N, + M, F, A, + get()]), + case net_adm:ping(N) of + pong -> + ?PRINT2("tc_try -> ~p still running - start runner~n", [N]), + OldFlag = trap_exit(true), % Make sure we catch it + Runner = spawn_link(N, ?MODULE, tc_wait, [self(), get(), M, F, A]), + await_tc_runner_started(Runner, OldFlag), + await_tc_runner_done(Runner, OldFlag); + pang -> + ?EPRINT2("tc_try -> ~p *not* running~n", [N]), + skip({node_not_running, N}) + end. + +await_tc_runner_started(Runner, OldFlag) -> + ?PRINT2("await tc-runner (~p) start ack~n", [Runner]), receive - {done, {'EXIT', Rn}, Loc} -> - ?DBG("call -> done with exit: " - "~n Rn: ~p" - "~n Loc: ~p", [Rn, Loc]), + {'EXIT', Runner, Reason} -> + ?EPRINT2("TC runner start failed: " + "~n ~p~n", [Reason]), + exit({tx_runner_start_failed, Reason}); + {tc_runner_started, Runner} -> + ?PRINT2("TC runner start acknowledged~n"), + ok + after 10000 -> %% We should *really* not have to wait this long, but... + trap_exit(OldFlag), + unlink_and_flush_exit(Runner), + RunnerInfo = process_info(Runner), + ?EPRINT2("TC runner start timeout: " + "~n ~p", [RunnerInfo]), + %% If we don't get a start ack within 10 seconds, we are f*ed + exit(Runner, kill), + exit({tc_runner_start, timeout, RunnerInfo}) + end. + +await_tc_runner_done(Runner, OldFlag) -> + receive + {'EXIT', Runner, Reason} -> + %% This is not a normal (tc) failure (that is the clause below). + %% Instead the tc runner process crashed, for some reason. So + %% check if have got any system events, and if so, skip. + SysEvs = snmp_test_global_sys_monitor:events(), + if + (SysEvs =:= []) -> + ?EPRINT2("TC runner failed: " + "~n ~p~n", [Reason]), + exit({tx_runner_failed, Reason}); + true -> + ?EPRINT2("TC runner failed when we got system events: " + "~n Reason: ~p" + "~n Sys Events: ~p" + "~n", [Reason, SysEvs]), + skip([{reason, Reason}, {system_events, SysEvs}]) + end; + {tc_runner_done, Runner, {'EXIT', {skip, Reason}}, Loc} -> + ?PRINT2("call -> done with skip: " + "~n Reason: ~p" + "~n Loc: ~p" + "~n", [Reason, Loc]), + trap_exit(OldFlag), + unlink_and_flush_exit(Runner), + put(test_server_loc, Loc), + skip(Reason); + {tc_runner_done, Runner, {'EXIT', Rn}, Loc} -> + ?PRINT2("call -> done with exit: " + "~n Rn: ~p" + "~n Loc: ~p" + "~n", [Rn, Loc]), + trap_exit(OldFlag), + unlink_and_flush_exit(Runner), put(test_server_loc, Loc), exit(Rn); - {done, Ret, _Zed} -> + {tc_runner_done, Runner, Ret, _Zed} -> ?DBG("call -> done:" "~n Ret: ~p" "~n Zed: ~p", [Ret, _Zed]), + trap_exit(OldFlag), + unlink_and_flush_exit(Runner), case Ret of {error, Reason} -> exit(Reason); + {skip, Reason} -> + skip(Reason); OK -> OK end end. -wait(From, Env, M, F, A) -> - ?DBG("wait -> entry with" - "~n From: ~p" - "~n Env: ~p" - "~n M: ~p" - "~n F: ~p" - "~n A: ~p", [From, Env, M, F, A]), +trap_exit(Flag) when is_boolean(Flag) -> + erlang:process_flag(trap_exit, Flag). + +unlink_and_flush_exit(Pid) -> + unlink(Pid), + receive + {'EXIT', Pid, _} -> + ok + after 0 -> + ok + end. + +tc_wait(From, Env, M, F, A) -> + ?PRINT2("tc_wait -> entry with" + "~n From: ~p" + "~n Env: ~p" + "~n M: ~p" + "~n F: ~p" + "~n A: ~p", [From, Env, M, F, A]), + From ! {tc_runner_started, self()}, lists:foreach(fun({K,V}) -> put(K,V) end, Env), - Rn = (catch apply(M, F, A)), - ?DBG("wait -> Rn: ~n~p", [Rn]), - From ! {done, Rn, get(test_server_loc)}, - exit(Rn). - -run(Mod, Func, Args, Opts) -> - ?DBG("run -> entry with" - "~n Mod: ~p" - "~n Func: ~p" - "~n Args: ~p" - "~n Opts: ~p", [Mod, Func, Args, Opts]), - M = get(mib_dir), - Dir = get(mgr_dir), - User = snmp_misc:get_option(user, Opts, "all-rights"), - SecLevel = snmp_misc:get_option(sec_level, Opts, noAuthNoPriv), - EngineID = snmp_misc:get_option(engine_id, Opts, "agentEngine"), + ?PRINT2("tc_wait -> env set - now run tc~n"), + Res = (catch apply(M, F, A)), + ?PRINT2("tc_wait -> tc run done: " + "~n ~p" + "~n", [Res]), + From ! {tc_runner_done, self(), Res, get(test_server_loc)}, + %% The point of this is that in some cases we have seen that the + %% exit signal having been "passed on" to the CT, which consider any + %% exit a fail (even if its {'EXIT', ok}). + %% So, just to be on the safe side, convert an 'ok' to a 'normal'. + case Res of + ok -> + exit(normal); + {ok, _} -> + exit(normal); + _ -> + exit(Res) + end. + +tc_run(Mod, Func, Args, Opts) -> + ?PRINT2("tc_run -> entry with" + "~n Mod: ~p" + "~n Func: ~p" + "~n Args: ~p" + "~n Opts: ~p" + "~n", [Mod, Func, Args, Opts]), + (catch snmp_test_mgr:stop()), % If we had a running mgr from a failed case + M = get(mib_dir), + Dir = get(mgr_dir), + User = snmp_misc:get_option(user, Opts, "all-rights"), + SecLevel = snmp_misc:get_option(sec_level, Opts, noAuthNoPriv), + EngineID = snmp_misc:get_option(engine_id, Opts, "agentEngine"), CtxEngineID = snmp_misc:get_option(context_engine_id, Opts, EngineID), - Community = snmp_misc:get_option(community, Opts, "all-rights"), - ?DBG("run -> start crypto app",[]), - _CryptoRes = ?CRYPTO_START(), - ?DBG("run -> Crypto: ~p", [_CryptoRes]), - catch snmp_test_mgr:stop(), % If we had a running mgr from a failed case - StdM = join(code:priv_dir(snmp), "mibs") ++ "/", - Vsn = get(vsn), - ?DBG("run -> config:" - "~n M: ~p" - "~n Vsn: ~p" - "~n Dir: ~p" - "~n User: ~p" - "~n SecLevel: ~p" - "~n EngineID: ~p" - "~n CtxEngineID: ~p" - "~n Community: ~p" - "~n StdM: ~p", - [M,Vsn,Dir,User,SecLevel,EngineID,CtxEngineID,Community,StdM]), + Community = snmp_misc:get_option(community, Opts, "all-rights"), + ?DBG("tc_run -> start crypto app",[]), + _CryptoRes = ?CRYPTO_START(), + ?DBG("tc_run -> Crypto: ~p", [_CryptoRes]), + StdM = join(code:priv_dir(snmp), "mibs") ++ "/", + Vsn = get(vsn), + ?PRINT2("tc_run -> config:" + "~n M: ~p" + "~n Vsn: ~p" + "~n Dir: ~p" + "~n User: ~p" + "~n SecLevel: ~p" + "~n EngineID: ~p" + "~n CtxEngineID: ~p" + "~n Community: ~p" + "~n StdM: ~p" + "~n", [M,Vsn,Dir,User,SecLevel,EngineID,CtxEngineID,Community,StdM]), case snmp_test_mgr:start([%% {agent, snmp_test_lib:hostname()}, {packet_server_debug, true}, {debug, true}, @@ -376,24 +486,45 @@ run(Mod, Func, Args, Opts) -> {mibs, mibs(StdM, M)}]) of {ok, _Pid} -> case (catch apply(Mod, Func, Args)) of + {'EXIT', {skip, Reason}} -> + ?EPRINT2("apply skip detected: " + "~n ~p", [Reason]), + (catch snmp_test_mgr:stop()), + ?SKIP(Reason); {'EXIT', Reason} -> - catch snmp_test_mgr:stop(), - ?FAIL({apply_failed, {Mod, Func, Args}, Reason}); + %% We have hosts (mostly *very* slooow VMs) that + %% can timeout anything. Since we are basically + %% testing communication, we therefor must check + %% for system events at every failure. Grrr! + SysEvs = snmp_test_global_sys_monitor:events(), + (catch snmp_test_mgr:stop()), + if + (SysEvs =:= []) -> + ?EPRINT2("TC runner failed: " + "~n ~p~n", [Reason]), + ?FAIL({apply_failed, {Mod, Func, Args}, Reason}); + true -> + ?EPRINT2("apply exit catched when we got system events: " + "~n Reason: ~p" + "~n Sys Events: ~p" + "~n", [Reason, SysEvs]), + ?SKIP([{reason, Reason}, {system_events, SysEvs}]) + end; Res -> - catch snmp_test_mgr:stop(), + (catch snmp_test_mgr:stop()), Res end; {error, Reason} -> ?EPRINT2("Failed starting (test) manager: " "~n ~p", [Reason]), - catch snmp_test_mgr:stop(), + (catch snmp_test_mgr:stop()), ?line ?FAIL({mgr_start_error, Reason}); Err -> ?EPRINT2("Failed starting (test) manager: " "~n ~p", [Err]), - catch snmp_test_mgr:stop(), + (catch snmp_test_mgr:stop()), ?line ?FAIL({mgr_start_failure, Err}) end. @@ -907,10 +1038,22 @@ expect2(Mod, Line, F) -> %% ---------------------------------------------------------------------- -get_timeout() -> - get_timeout(os:type()). +-define(BASE_REQ_TIMEOUT, 3500). -get_timeout(_) -> 3500. +get_timeout() -> + %% Try to figure out how "fast" a machine is. + %% We assume that the number of schedulers + %% (which depends on the number of core:s) + %% effect the performance of the host... + %% This is obviously not enough. The network + %% also matterns, clock freq or the CPU, ... + %% But its better than what we had before... + case erlang:system_info(schedulers) of + N when is_integer(N) -> + ?BASE_REQ_TIMEOUT + timer:seconds(10 div N); + _ -> + ?BASE_REQ_TIMEOUT + end. receive_pdu(To) -> receive @@ -1083,6 +1226,18 @@ do_expect(trap, Enterp, Generic, Specific, ExpVBs, To) -> {PureE, Generic, Specific, ExpVBs}, {Ent2, G2, Spec2, VBs}}}; + {error, timeout} = Error -> + SysEvs = snmp_test_global_sys_monitor:events(), + io_format_expect("[expecting trap] got timeout when system events:" + "~n ~p", [SysEvs]), + if + (SysEvs =:= []) -> + Error; + true -> + skip({system_events, SysEvs}) + end; + + Error -> Error end. @@ -1184,7 +1339,7 @@ do_expect2(Check, Type, Err, Idx, ExpVBs, To) io_format_expect("received unexpected pdu with (11) " "~n Type: ~p" "~n ReqId: ~p" - "~n Errot status: ~p" + "~n Error status: ~p" "~n Error index: ~p", [Type2, ReqId, Err2, Idx2]), {error, @@ -1247,7 +1402,7 @@ do_expect2(Check, Type, Err, Idx, ExpVBs, To) io_format_expect("received unexpected pdu with (15) " "~n Type: ~p" "~n ReqId: ~p" - "~n Errot status: ~p" + "~n Error status: ~p" "~n Error index: ~p" "~n Varbinds: ~p", [Type2, ReqId, Err2, Idx2, VBs2]), @@ -1257,10 +1412,23 @@ do_expect2(Check, Type, Err, Idx, ExpVBs, To) {Type2, Err2, Idx2, VBs2}, ReqId}}; - Error -> - io_format_expect("received error (16): " + + {error, timeout} = Error -> + SysEvs = snmp_test_global_sys_monitor:events(), + io_format_expect("got timeout (16) when system events:" + "~n ~p", [SysEvs]), + if + (SysEvs =:= []) -> + Error; + true -> + skip({system_events, SysEvs}) + end; + + + Error -> + io_format_expect("received error (17): " "~n Error: ~p", [Error]), - Error + Error end. @@ -1378,12 +1546,15 @@ start_node(Name) -> "" end, %% Do not use start_link!!! (the proc that calls this one is tmp) - ?DBG("start_node -> Args: ~p~n",[Args]), - A = Args ++ " -pa " ++ Pa, + ?DBG("start_node -> Args: ~p~n", [Args]), + A = Args ++ " -pa " ++ Pa ++ + " -s " ++ atom_to_list(snmp_test_sys_monitor) ++ " start" ++ + " -s global sync", case (catch ?START_NODE(Name, A)) of {ok, Node} -> %% Tell the test_server to not clean up things it never started. ?DBG("start_node -> Node: ~p",[Node]), + global:sync(), {ok, Node}; Else -> ?ERR("start_node -> failed with(other): Else: ~p",[Else]), @@ -1701,6 +1872,10 @@ rpc(Node, F, A) -> join(Dir, File) -> filename:join(Dir, File). + +skip(R) -> + exit({skip, R}). + %% await_pdu(To) -> %% await_response(To, pdu). %% diff --git a/lib/snmp/test/snmp_manager_config_test.erl b/lib/snmp/test/snmp_manager_config_test.erl index 64d3134055..ccbdd77629 100644 --- a/lib/snmp/test/snmp_manager_config_test.erl +++ b/lib/snmp/test/snmp_manager_config_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2016. All Rights Reserved. +%% Copyright Ericsson AB 2004-2019. 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. @@ -35,6 +35,7 @@ -include_lib("common_test/include/ct.hrl"). -include("snmp_test_lib.hrl"). -include_lib("snmp/src/manager/snmpm_usm.hrl"). +-include_lib("snmp/src/app/snmp_internal.hrl"). %%---------------------------------------------------------------------- @@ -2259,11 +2260,13 @@ create_and_increment(Conf) when is_list(Conf) -> ?line {ok, _Pid} = snmpm_config:start_link(Opts), %% Random init - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), - StartVal = random:uniform(2147483647), + StartVal = rand:uniform(2147483647), IncVal = 42, EndVal = StartVal + IncVal, diff --git a/lib/snmp/test/snmp_manager_test.erl b/lib/snmp/test/snmp_manager_test.erl index 5b0ebf8647..c31bb92e1f 100644 --- a/lib/snmp/test/snmp_manager_test.erl +++ b/lib/snmp/test/snmp_manager_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2003-2017. All Rights Reserved. +%% Copyright Ericsson AB 2003-2019. 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. @@ -62,7 +62,7 @@ register_user1/1, - register_agent1/1, + register_agent_old/1, register_agent2/1, register_agent3/1, @@ -185,7 +185,6 @@ end_per_suite(Config) when is_list(Config) -> init_per_testcase(Case, Config) when is_list(Config) -> - io:format(user, "~n~n*** INIT ~w:~w ***~n~n", [?MODULE, Case]), p(Case, "init_per_testcase begin when" "~n Nodes: ~p~n~n", [erlang:nodes()]), %% This version of the API, based on Addr and Port, has been deprecated @@ -204,13 +203,15 @@ init_per_testcase(Case, Config) when is_list(Config) -> Result = case lists:member(Case, DeprecatedApiCases) of true -> - {skip, api_no_longer_supported}; + {skip, "API no longer supported"}; false -> try init_per_testcase2(Case, Config) catch - C:{skip, _} = E:_ when ((C =:= throw) orelse (C =:= exit)) -> + C:{skip, _} = E:_ when ((C =:= throw) orelse + (C =:= exit)) -> E; - C:E:_ when ((C =:= throw) orelse (C =:= exit)) -> + C:E:_ when ((C =:= throw) orelse + (C =:= exit)) -> {skip, {catched, C, E}} end end, @@ -479,7 +480,7 @@ groups() -> }, {agent_tests, [], [ - register_agent1, + register_agent_old, register_agent2, register_agent3 ] @@ -595,7 +596,7 @@ groups() -> ipv6_tests() -> [ - register_agent1, + register_agent_old, simple_sync_get_next3, simple_async_get2, simple_sync_get3, @@ -619,38 +620,47 @@ init_per_group(event_tests_mt = GroupName, Config) -> GroupName, [{manager_net_if_module, snmpm_net_if_mt} | Config]); init_per_group(ipv6_mt = GroupName, Config) -> - {ok, Hostname0} = inet:gethostname(), - case ct:require(ipv6_hosts) of - ok -> - case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of - true -> - ipv6_init( - snmp_test_lib:init_group_top_dir( - GroupName, - [{manager_net_if_module, snmpm_net_if_mt} - | Config])); - false -> - {skip, "Host does not support IPv6"} - end; - _ -> - {skip, "Test config ipv6_hosts is missing"} - end; + init_per_group_ipv6(GroupName, + [{manager_net_if_module, snmpm_net_if_mt} | Config]); init_per_group(ipv6 = GroupName, Config) -> - {ok, Hostname0} = inet:gethostname(), - case ct:require(ipv6_hosts) of - ok -> - case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of - true -> - ipv6_init(snmp_test_lib:init_group_top_dir(GroupName, Config)); - false -> - {skip, "Host does not support IPv6"} - end; - _ -> - {skip, "Test config ipv6_hosts is missing"} - end; + init_per_group_ipv6(GroupName, Config); init_per_group(GroupName, Config) -> snmp_test_lib:init_group_top_dir(GroupName, Config). + +init_per_group_ipv6(GroupName, Config) -> + %% <OS-CONDITIONAL-SKIP> + OSSkipable = [{unix, + [ + {darwin, fun(V) when (V > {9, 8, 0}) -> + %% This version is OK: No Skip + false; + (_) -> + %% This version is *not* ok: Skip + %% We need a fully qualified hostname + %% to get a proper IPv6 address (in this + %% version), but its just to messy, so + %% instead we skip this **OLD** darwin... + true + end} + ] + }], + %% </OS-CONDITIONAL-SKIP> + case ?OS_BASED_SKIP(OSSkipable) of + true -> + {skip, "Host *may* not *properly* support IPV6"}; + false -> + %% Even if this host supports IPv6 we don't use it unless its + %% one of the configures/supported IPv6 hosts... + case (?HAS_SUPPORT_IPV6() andalso ?IS_IPV6_HOST()) of + true -> + ipv6_init(snmp_test_lib:init_group_top_dir(GroupName, Config)); + false -> + {skip, "Host does not support IPv6"} + end + end. + + end_per_group(_GroupName, Config) -> %% Do we really need to do this? lists:keydelete(snmp_group_top_dir, 1, Config). @@ -662,11 +672,11 @@ end_per_group(_GroupName, Config) -> simple_start_and_stop1(suite) -> []; simple_start_and_stop1(Config) when is_list(Config) -> - %% ?SKIP(not_yet_implemented), - process_flag(trap_exit, true), - put(tname,ssas1), - p("starting with Config: ~n~p", [Config]), + ?TC_TRY(simple_start_and_stop1, + fun() -> do_simple_start_and_stop1(Config) end). +do_simple_start_and_stop1(Config) -> + p("starting with Config: ~n~p", [Config]), ConfDir = ?config(manager_conf_dir, Config), DbDir = ?config(manager_db_dir, Config), @@ -687,7 +697,6 @@ simple_start_and_stop1(Config) when is_list(Config) -> ?SLEEP(1000), - p("end"), ok. @@ -695,9 +704,10 @@ simple_start_and_stop1(Config) when is_list(Config) -> simple_start_and_stop2(suite) -> []; simple_start_and_stop2(Config) when is_list(Config) -> - %% ?SKIP(not_yet_implemented), - process_flag(trap_exit, true), - put(tname,ssas2), + ?TC_TRY(simple_start_and_stop2, + fun() -> do_simple_start_and_stop2(Config) end). + +do_simple_start_and_stop2(Config) -> p("starting with Config: ~p~n", [Config]), ManagerNode = start_manager_node(), @@ -735,7 +745,6 @@ simple_start_and_stop2(Config) when is_list(Config) -> ?SLEEP(1000), - p("end"), ok. @@ -743,8 +752,10 @@ simple_start_and_stop2(Config) when is_list(Config) -> simple_start_and_monitor_crash1(suite) -> []; simple_start_and_monitor_crash1(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname,ssamc1), + ?TC_TRY(simple_start_and_monitor_crash1, + fun() -> do_simple_start_and_monitor_crash1(Config) end). + +do_simple_start_and_monitor_crash1(Config) -> p("starting with Config: ~n~p", [Config]), ConfDir = ?config(manager_conf_dir, Config), @@ -788,7 +799,6 @@ simple_start_and_monitor_crash1(Config) when is_list(Config) -> ?FAIL(timeout) end, - p("end"), ok. @@ -796,8 +806,10 @@ simple_start_and_monitor_crash1(Config) when is_list(Config) -> simple_start_and_monitor_crash2(suite) -> []; simple_start_and_monitor_crash2(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname,ssamc2), + ?TC_TRY(simple_start_and_monitor_crash2, + fun() -> do_simple_start_and_monitor_crash2(Config) end). + +do_simple_start_and_monitor_crash2(Config) -> p("starting with Config: ~n~p", [Config]), ConfDir = ?config(manager_conf_dir, Config), @@ -842,7 +854,6 @@ simple_start_and_monitor_crash2(Config) when is_list(Config) -> ?FAIL(timeout) end, - p("end"), ok. @@ -888,8 +899,10 @@ simulate_crash(NumKills, _) -> notify_started01(suite) -> []; notify_started01(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname,ns01), + ?TC_TRY(notify_started01, + fun() -> do_notify_started01(Config) end). + +do_notify_started01(Config) -> p("starting with Config: ~n~p", [Config]), ConfDir = ?config(manager_conf_dir, Config), @@ -976,37 +989,34 @@ snmpm_starter(Opts, To) -> notify_started02(suite) -> []; notify_started02(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname,ns02), + ?TC_TRY(notify_started02, + fun() -> notify_started02_cond(Config) end, + fun() -> do_notify_started02(Config) end). - %% <CONDITIONAL-SKIP> - %% The point of this is to catch machines running - %% SLES9 (2.6.5) +notify_started02_cond(Config) -> LinuxVersionVerify = fun() -> case os:cmd("uname -m") of "i686" ++ _ -> -%% io:format("found an i686 machine, " -%% "now check version~n", []), case os:version() of {2, 6, Rev} when Rev >= 16 -> - true; + false; {2, Min, _} when Min > 6 -> - true; + false; {Maj, _, _} when Maj > 2 -> - true; + false; _ -> - false + true end; _ -> - true + false end end, Skippable = [{unix, [{linux, LinuxVersionVerify}]}], Condition = fun() -> ?OS_BASED_SKIP(Skippable) end, - ?NON_PC_TC_MAYBE_SKIP(Config, Condition), - %% </CONDITIONAL-SKIP> - + ?NON_PC_TC_MAYBE_SKIP(Config, Condition). + +do_notify_started02(Config) -> p("starting with Config: ~n~p", [Config]), ConfDir = ?config(manager_conf_dir, Config), @@ -1014,10 +1024,10 @@ notify_started02(Config) when is_list(Config) -> write_manager_conf(ConfDir), - Opts = [{server, [{verbosity, log}]}, - {net_if, [{verbosity, silence}]}, + Opts = [{server, [{verbosity, log}]}, + {net_if, [{verbosity, silence}]}, {note_store, [{verbosity, silence}]}, - {config, [{verbosity, log}, {dir, ConfDir}, {db_dir, DbDir}]}], + {config, [{verbosity, debug}, {dir, ConfDir}, {db_dir, DbDir}]}], p("start snmpm client process"), NumIterations = 5, @@ -1047,8 +1057,14 @@ notify_started02(Config) when is_list(Config) -> p("await snmpm client process exit (max ~p+10000 msec)", [ApproxStartTime]), receive + %% We take this opportunity to check if we got a skip from + %% the ctrl process. + {'EXIT', Pid2, {skip, SkipReason1}} -> + ?SKIP(SkipReason1); {'EXIT', Pid1, normal} -> ok; + {'EXIT', Pid1, {suite_failed, Reason1}} -> + ?FAIL({client, Reason1}); {'EXIT', Pid1, Reason1} -> ?FAIL({client, Reason1}) after ApproxStartTime + 10000 -> @@ -1061,6 +1077,9 @@ notify_started02(Config) when is_list(Config) -> receive {'EXIT', Pid2, normal} -> ok; + {'EXIT', Pid2, {skip, SkipReason2}} -> + %% In case of a race + ?SKIP(SkipReason2); {'EXIT', Pid2, Reason2} -> ?FAIL({ctrl, Reason2}) after 5000 -> @@ -1085,7 +1104,7 @@ ns02_client_await_approx_runtime(Pid) -> "~n ~p", [Pid, Reason]), {error, Reason} - after 15000 -> + after 30000 -> %% Either something is *really* wrong or this machine %% is dog slow. Either way, this is a skip-reason... {skip, approx_runtime_timeout} @@ -1150,6 +1169,12 @@ ns02_ctrl(Opts, N) -> p("starting"), ns02_ctrl_loop(Opts, N). + +%% We have seen that some times it takes unreasonably long time to +%% start the manager (it got "stuck" in snmpm_config). But since +%% we did not have enough verbosity, we do not know how far it got. +%% So, we try to monitor each start attempt. We allow 5 sec (just +%% to give slow boxes a chance). ns02_ctrl_loop(_Opts, 0) -> p("done"), exit(normal); @@ -1157,19 +1182,45 @@ ns02_ctrl_loop(Opts, N) -> p("entry when N: ~p", [N]), ?SLEEP(2000), p("start manager"), - snmpm:start(Opts), + TS1 = erlang:system_time(millisecond), + {StarterPid, StarterMRef} = + erlang:spawn_monitor(fun() -> exit(snmpm:start(Opts)) end), + receive + {'DOWN', StarterMRef, process, StarterPid, ok} -> + TS2 = erlang:system_time(millisecond), + p("manager started: ~w ms", [TS2-TS1]), + ok + after 5000 -> + p("manager (~p) start timeout - kill", [StarterPid]), + exit(StarterPid, kill), + exit({skip, start_timeout}) + end, ?SLEEP(2000), p("stop manager"), - snmpm:stop(), + ?SLEEP(100), % Give the verbosity to take effect... + TS3 = erlang:system_time(millisecond), + case snmpm:stop(5000) of + ok -> + TS4 = erlang:system_time(millisecond), + p("manager stopped: ~p ms", [TS4-TS3]), + ok; + {error, timeout} -> + p("manager stop timeout - kill (cleanup) and skip"), + exit(whereis(snmpm_supervisor), kill), + exit({skip, stop_timeout}) + end, ns02_ctrl_loop(Opts, N-1). + %%====================================================================== info(suite) -> []; info(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname,info), + ?TC_TRY(info, + fun() -> do_info(Config) end). + +do_info(Config) -> p("starting with Config: ~n~p", [Config]), ConfDir = ?config(manager_conf_dir, Config), @@ -1197,7 +1248,6 @@ info(Config) when is_list(Config) -> ?SLEEP(1000), - p("end"), ok. verify_info(Info) when is_list(Info) -> @@ -1237,9 +1287,10 @@ verify_info([{Key, SubKeys}|Keys], Info) -> register_user1(suite) -> []; register_user1(Config) when is_list(Config) -> - %% ?SKIP(not_yet_implemented). - process_flag(trap_exit, true), - put(tname,ru1), + ?TC_TRY(register_user1, + fun() -> do_register_user1(Config) end). + +do_register_user1(Config) -> p("starting with Config: ~p~n", [Config]), ManagerNode = start_manager_node(), @@ -1313,7 +1364,6 @@ register_user1(Config) when is_list(Config) -> ?SLEEP(1000), - p("end"), ok. verify_users([], []) -> @@ -1331,14 +1381,15 @@ verify_users(ActualUsers0, [User|RegUsers]) -> %%====================================================================== -register_agent1(doc) -> +register_agent_old(doc) -> ["Test registration of agents with the OLD interface functions"]; -register_agent1(suite) -> +register_agent_old(suite) -> []; -register_agent1(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname,ra1), - +register_agent_old(Config) when is_list(Config) -> + ?TC_TRY(register_agent_old, + fun() -> do_register_agent_old(Config) end). + +do_register_agent_old(Config) -> p("starting with Config: ~p~n", [Config]), ManagerNode = start_manager_node(), @@ -1453,7 +1504,6 @@ register_agent1(Config) when is_list(Config) -> ?SLEEP(1000), - p("end"), ok. @@ -1464,8 +1514,10 @@ register_agent2(doc) -> register_agent2(suite) -> []; register_agent2(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, ra2), + ?TC_TRY(register_agent2, + fun() -> do_register_agent2(Config) end). + +do_register_agent2(Config) -> p("starting with Config: ~p~n", [Config]), ManagerNode = start_manager_node(), @@ -1598,7 +1650,6 @@ register_agent2(Config) when is_list(Config) -> ?SLEEP(1000), - p("end"), ok. @@ -1610,8 +1661,10 @@ register_agent3(doc) -> register_agent3(suite) -> []; register_agent3(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, ra3), + ?TC_TRY(register_agent3, + fun() -> do_register_agent3(Config) end). + +do_register_agent3(Config) -> p("starting with Config: ~p~n", [Config]), ManagerNode = start_manager_node(), @@ -1748,7 +1801,6 @@ register_agent3(Config) when is_list(Config) -> ?SLEEP(1000), - p("end"), ok. @@ -1757,10 +1809,11 @@ register_agent3(Config) when is_list(Config) -> simple_sync_get1(doc) -> ["Simple sync get-request - Old style (Addr & Port)"]; simple_sync_get1(suite) -> []; simple_sync_get1(Config) when is_list(Config) -> - ?SKIP(api_no_longer_supported), + ?TC_TRY(simple_sync_get1, + fun() -> {skip, "API no longer supported"} end, + fun() -> do_simple_sync_get1(Config) end). - process_flag(trap_exit, true), - put(tname, ssg1), +do_simple_sync_get1(Config) -> p("starting with Config: ~p~n", [Config]), Node = ?config(manager_node, Config), @@ -1819,18 +1872,18 @@ simple_sync_get2(doc) -> ["Simple sync get-request - Version 2 API (TargetName)"]; simple_sync_get2(suite) -> []; simple_sync_get2(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, ssg2), - do_simple_sync_get2(Config), - display_log(Config), - ok. + ?TC_TRY(simple_sync_get2, + fun() -> do_simple_sync_get2(Config) end). do_simple_sync_get2(Config) -> + p("starting with Config: ~n~p", [Config]), Get = fun(Node, TargetName, Oids) -> mgr_user_sync_get(Node, TargetName, Oids) end, PostVerify = fun() -> ok end, - do_simple_sync_get2(Config, Get, PostVerify). + do_simple_sync_get2(Config, Get, PostVerify), + display_log(Config), + ok. do_simple_sync_get2(Config, Get, PostVerify) -> p("starting with Config: ~p~n", [Config]), @@ -1886,13 +1939,11 @@ simple_sync_get3(doc) -> ["Simple sync get-request - Version 3 API (TargetName and send-opts)"]; simple_sync_get3(suite) -> []; simple_sync_get3(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, ssg3), - do_simple_sync_get3(Config), - display_log(Config), - ok. + ?TC_TRY(simple_sync_get3, + fun() -> do_simple_sync_get3(Config) end). do_simple_sync_get3(Config) -> + p("starting with Config: ~n~p", [Config]), Self = self(), Msg = simple_sync_get3, Fun = fun() -> Self ! Msg end, @@ -1911,7 +1962,9 @@ do_simple_sync_get3(Config) -> ok end end, - do_simple_sync_get2(Config, Get, PostVerify). + do_simple_sync_get2(Config, Get, PostVerify), + display_log(Config), + ok. %%====================================================================== @@ -1920,10 +1973,11 @@ simple_async_get1(doc) -> ["Simple (async) get-request - Old style (Addr & Port)"]; simple_async_get1(suite) -> []; simple_async_get1(Config) when is_list(Config) -> - ?SKIP(api_no_longer_supported), + ?TC_TRY(simple_async_get1, + fun() -> {skip, "API no longer supported"} end, + fun() -> do_simple_async_get1(Config) end). - process_flag(trap_exit, true), - put(tname, sag1), +do_simple_async_get1(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -2024,8 +2078,10 @@ simple_async_get2(doc) -> ["Simple (async) get-request - Version 2 API (TargetName)"]; simple_async_get2(suite) -> []; simple_async_get2(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, sag2), + ?TC_TRY(simple_async_get2, + fun() -> do_simple_async_get2(Config) end). + +do_simple_async_get2(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -2105,8 +2161,10 @@ simple_async_get3(doc) -> ["Simple (async) get-request - Version 3 API (TargetName and send-opts)"]; simple_async_get3(suite) -> []; simple_async_get3(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, sag3), + ?TC_TRY(simple_async_get3, + fun() -> do_simple_async_get3(Config) end). + +do_simple_async_get3(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), AgentNode = ?config(agent_node, Config), @@ -2137,10 +2195,11 @@ simple_sync_get_next1(doc) -> ["Simple (sync) get_next-request - " "Old style (Addr & Port)"]; simple_sync_get_next1(suite) -> []; simple_sync_get_next1(Config) when is_list(Config) -> - ?SKIP(api_no_longer_supported), + ?TC_TRY(simple_sync_get_next1, + fun() -> {skip, "API no longer supported"} end, + fun() -> do_simple_sync_get_next1(Config) end). - process_flag(trap_exit, true), - put(tname, ssgn1), +do_simple_sync_get_next1(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -2278,8 +2337,10 @@ simple_sync_get_next2(doc) -> ["Simple (sync) get_next-request - Version 2 API (TargetName)"]; simple_sync_get_next2(suite) -> []; simple_sync_get_next2(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, ssgn2), + ?TC_TRY(simple_sync_get_next2, + fun() -> do_simple_sync_get_next2(Config) end). + +do_simple_sync_get_next2(Config) -> p("starting with Config: ~p~n", [Config]), GetNext = fun(Node, TargetName, Oids) -> @@ -2431,10 +2492,11 @@ simple_async_get_next1(doc) -> ["Simple (async) get_next-request - " "Old style (Addr & Port)"]; simple_async_get_next1(suite) -> []; simple_async_get_next1(Config) when is_list(Config) -> - ?SKIP(api_no_longer_supported), + ?TC_TRY(simple_async_get_next1, + fun() -> {skip, "API no longer supported"} end, + fun() -> do_simple_async_get_next1(Config) end). - process_flag(trap_exit, true), - put(tname, ssgn1), +do_simple_async_get_next1(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -2531,8 +2593,10 @@ simple_async_get_next2(doc) -> ["Simple (async) get_next-request - Version 2 API (TargetName)"]; simple_async_get_next2(suite) -> []; simple_async_get_next2(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, ssgn2), + ?TC_TRY(simple_async_get_next2, + fun() -> do_simple_async_get_next2(Config) end). + +do_simple_async_get_next2(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -2642,8 +2706,10 @@ simple_async_get_next3(doc) -> "Version 3 API (TargetName with send-opts)"]; simple_async_get_next3(suite) -> []; simple_async_get_next3(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, ssgn2), + ?TC_TRY(simple_async_get_next3, + fun() -> do_simple_async_get_next3(Config) end). + +do_simple_async_get_next3(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -2685,10 +2751,11 @@ simple_sync_set1(doc) -> ["Simple (sync) set-request - " "Old style (Addr & Port)"]; simple_sync_set1(suite) -> []; simple_sync_set1(Config) when is_list(Config) -> - ?SKIP(api_no_longer_supported), + ?TC_TRY(simple_sync_set1, + fun() -> {skip, "API no longer supported"} end, + fun() -> do_simple_sync_set1(Config) end). - process_flag(trap_exit, true), - put(tname, sss1), +do_simple_sync_set1(Config) -> p("starting with Config: ~p~n", [Config]), Node = ?config(manager_node, Config), @@ -2758,8 +2825,10 @@ simple_sync_set2(doc) -> ["Simple (sync) set-request - Version 2 API (TargetName)"]; simple_sync_set2(suite) -> []; simple_sync_set2(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, sss2), + ?TC_TRY(simple_sync_set2, + fun() -> do_simple_sync_set2(Config) end). + +do_simple_sync_set2(Config) -> p("starting with Config: ~p~n", [Config]), Set = fun(Node, TargetName, VAVs) -> @@ -2828,8 +2897,10 @@ simple_sync_set3(doc) -> ["Simple (sync) set-request - Version 3 API (TargetName with send-opts)"]; simple_sync_set3(suite) -> []; simple_sync_set3(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, sss3), + ?TC_TRY(simple_sync_set3, + fun() -> do_simple_sync_set3(Config) end). + +do_simple_sync_set3(Config) -> p("starting with Config: ~p~n", [Config]), Self = self(), @@ -2857,10 +2928,11 @@ simple_async_set1(doc) -> ["Simple (async) set-request - " "Old style (Addr & Port)"]; simple_async_set1(suite) -> []; simple_async_set1(Config) when is_list(Config) -> - ?SKIP(api_no_longer_supported), + ?TC_TRY(simple_async_set1, + fun() -> {skip, "API no longer supported"} end, + fun() -> do_simple_async_set1(Config) end). - process_flag(trap_exit, true), - put(tname, sas1), +do_simple_async_set1(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -2952,8 +3024,10 @@ simple_async_set2(doc) -> ["Simple (async) set-request - Version 2 API (TargetName)"]; simple_async_set2(suite) -> []; simple_async_set2(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, sas2), + ?TC_TRY(simple_async_set2, + fun() -> do_simple_async_set2(Config) end). + +do_simple_async_set2(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -3025,8 +3099,10 @@ simple_async_set3(doc) -> ["Simple (async) set-request - Version 3 API (TargetName with send-opts)"]; simple_async_set3(suite) -> []; simple_async_set3(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, sas3), + ?TC_TRY(simple_async_set3, + fun() -> do_simple_async_set3(Config) end). + +do_simple_async_set3(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -3069,10 +3145,11 @@ simple_sync_get_bulk1(doc) -> ["Simple (sync) get_bulk-request - " "Old style (Addr & Port)"]; simple_sync_get_bulk1(suite) -> []; simple_sync_get_bulk1(Config) when is_list(Config) -> - ?SKIP(api_no_longer_supported), + ?TC_TRY(simple_sync_get_bulk1, + fun() -> {skip, "API no longer supported"} end, + fun() -> do_simple_sync_get_bulk1(Config) end). - process_flag(trap_exit, true), - put(tname, ssgb1), +do_simple_sync_get_bulk1(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -3243,8 +3320,10 @@ simple_sync_get_bulk2(doc) -> ["Simple (sync) get_bulk-request - Version 2 API (TargetName)"]; simple_sync_get_bulk2(suite) -> []; simple_sync_get_bulk2(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, ssgb2), + ?TC_TRY(simple_sync_get_bulk2, + fun() -> do_simple_sync_get_bulk2(Config) end). + +do_simple_sync_get_bulk2(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -3399,8 +3478,10 @@ simple_sync_get_bulk3(doc) -> "Version 3 API (TargetName with send-opts)"]; simple_sync_get_bulk3(suite) -> []; simple_sync_get_bulk3(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, ssgb3), + ?TC_TRY(simple_sync_get_bulk3, + fun() -> do_simple_sync_get_bulk3(Config) end). + +do_simple_sync_get_bulk3(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -3436,10 +3517,11 @@ simple_async_get_bulk1(doc) -> ["Simple (async) get_bulk-request - " "Old style (Addr & Port)"]; simple_async_get_bulk1(suite) -> []; simple_async_get_bulk1(Config) when is_list(Config) -> - ?SKIP(api_no_longer_supported), + ?TC_TRY(simple_async_get_bulk1, + fun() -> {skip, "API no longer supported"} end, + fun() -> do_simple_async_get_bulk1(Config) end). - process_flag(trap_exit, true), - put(tname, sagb1), +do_simple_async_get_bulk1(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -3582,8 +3664,10 @@ simple_async_get_bulk2(doc) -> ["Simple (async) get_bulk-request - Version 2 API (TargetName)"]; simple_async_get_bulk2(suite) -> []; simple_async_get_bulk2(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, sagb2), + ?TC_TRY(simple_async_get_bulk2, + fun() -> do_simple_async_get_bulk2(Config) end). + +do_simple_async_get_bulk2(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -3738,8 +3822,10 @@ simple_async_get_bulk3(doc) -> "Version 3 API (TargetName with send-opts)"]; simple_async_get_bulk3(suite) -> []; simple_async_get_bulk3(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, sagb3), + ?TC_TRY(simple_async_get_bulk3, + fun() -> do_simple_async_get_bulk3(Config) end). + +do_simple_async_get_bulk3(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -3782,10 +3868,11 @@ misc_async1(doc) -> ["Misc (async) request(s) - " "Old style (Addr & Port)"]; misc_async1(suite) -> []; misc_async1(Config) when is_list(Config) -> - ?SKIP(api_no_longer_supported), + ?TC_TRY(misc_async1, + fun() -> {skip, "API no longer supported"} end, + fun() -> do_misc_async1(Config) end). - process_flag(trap_exit, true), - put(tname, ms1), +do_misc_async1(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -3974,8 +4061,10 @@ misc_async2(doc) -> ["Misc (async) request(s) - Version 2 API (TargetName)"]; misc_async2(suite) -> []; misc_async2(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, ms2), + ?TC_TRY(misc_async2, + fun() -> do_misc_async2(Config) end). + +do_misc_async2(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -4224,8 +4313,10 @@ verify_trap(Trap, [{Id, Verifier}|Verifiers]) -> trap1(suite) -> []; trap1(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname,t1), + ?TC_TRY(trap1, + fun() -> do_trap1(Config) end). + +do_trap1(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -4377,8 +4468,10 @@ trap1(Config) when is_list(Config) -> trap2(suite) -> []; trap2(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname,t2), + ?TC_TRY(trap2, + fun() -> do_trap2(Config) end). + +do_trap2(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -4570,8 +4663,10 @@ trap2(Config) when is_list(Config) -> inform1(suite) -> []; inform1(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname,i1), + ?TC_TRY(inform1, + fun() -> do_inform1(Config) end). + +do_inform1(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -4697,8 +4792,10 @@ inform1(Config) when is_list(Config) -> inform2(suite) -> []; inform2(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, i2), + ?TC_TRY(inform2, + fun() -> do_inform2(Config) end). + +do_inform2(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -4832,7 +4929,7 @@ inform2(Config) when is_list(Config) -> "~n ~p", [Addr]), ok; {snmp_notification, inform2_tag1, {no_response, Addr}} -> - p("<ERROR> received expected \"no response\" " + e("Received unexpected \"no response\" " "notification from: " "~n ~p", [Addr]), {error, no_response} @@ -4869,8 +4966,10 @@ inform2(Config) when is_list(Config) -> inform3(suite) -> []; inform3(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname,i3), + ?TC_TRY(inform3, + fun() -> do_inform3(Config) end). + +do_inform3(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -4966,7 +5065,7 @@ inform3(Config) when is_list(Config) -> "~n ~p", [Addr]), ok; {snmp_notification, inform3_tag1, {got_response, Addr}} -> - p("<ERROR> received unexpected \"got response\" " + e("Received unexpected \"got response\" " "notification from: " "~n ~p", [Addr]), @@ -5005,8 +5104,10 @@ inform3(Config) when is_list(Config) -> inform4(suite) -> []; inform4(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname,i4), + ?TC_TRY(inform4, + fun() -> do_inform4(Config) end). + +do_inform4(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -5125,8 +5226,10 @@ inform4(Config) when is_list(Config) -> inform_swarm(suite) -> []; inform_swarm(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, is), + ?TC_TRY(inform_swarm, + fun() -> do_inform_swarm(Config) end). + +do_inform_swarm(Config) -> p("starting with Config: ~p~n", [Config]), MgrNode = ?config(manager_node, Config), @@ -5253,7 +5356,7 @@ inform_swarm_collector(N, SentAckCnt, RecvCnt, RespCnt, Timeout) -> inform_swarm_collector(N, SentAckCnt, RecvCnt+1, RespCnt, Timeout); {Err, Idx, VBs} -> - p("<ERROR> unexpected error status: " + e("Unexpected error status: " "~n Err: ~p" "~n Idx: ~p" "~n VBs: ~p", [Err, Idx, VBs]), @@ -5272,7 +5375,7 @@ inform_swarm_collector(N, SentAckCnt, RecvCnt, RespCnt, Timeout) -> %% The agent did not received ack from the manager in time {snmp_notification, inform2_tag1, {no_response, Addr}} -> - p("<ERROR> received expected \"no response\" notification " + e("Received expected \"no response\" notification " "from: " "~n ~p", [Addr]), Reason = {no_response, Addr, {N, SentAckCnt, RecvCnt, RespCnt}}, @@ -5297,8 +5400,10 @@ report(Config) when is_list(Config) -> otp8015_1(doc) -> ["OTP-8015:1 - testing the new api-function."]; otp8015_1(suite) -> []; otp8015_1(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, otp8015_1), + ?TC_TRY(otp8015_1, + fun() -> do_otp8015_1(Config) end). + +do_otp8015_1(Config) -> p("starting with Config: ~p~n", [Config]), ConfDir = ?config(manager_conf_dir, Config), @@ -5345,8 +5450,10 @@ otp8015_1(Config) when is_list(Config) -> otp8395_1(doc) -> ["OTP-8395:1 - simple get with ATL sequence numbering."]; otp8395_1(suite) -> []; otp8395_1(Config) when is_list(Config) -> - process_flag(trap_exit, true), - put(tname, otp8395_1), + ?TC_TRY(otp8395_1, + fun() -> do_otp8395_1(Config) end). + +do_otp8395_1(Config) -> do_simple_sync_get2(Config). @@ -5449,10 +5556,10 @@ command_handler([{No, Desc, Cmd}|Cmds]) -> p("command_handler -> ~w: ok",[No]), command_handler(Cmds); {error, Reason} -> - p("<ERROR> command_handler -> ~w error: ~n~p",[No, Reason]), + e("Command_handler -> ~w error: ~n~p",[No, Reason]), ?line ?FAIL({command_failed, No, Reason}); Error -> - p("<ERROR> command_handler -> ~w unexpected: ~n~p",[No, Error]), + e("Command_handler -> ~w unexpected: ~n~p",[No, Error]), ?line ?FAIL({unexpected_command_result, No, Error}) end. @@ -6318,6 +6425,8 @@ start_manager_node() -> start_node(snmp_manager). start_node(Name) -> + start_node(Name, true). +start_node(Name, Retry) -> Pa = filename:dirname(code:which(?MODULE)), Args = case init:get_argument('CC_TEST') of {ok, [[]]} -> @@ -6328,30 +6437,47 @@ start_node(Name) -> "" end, A = Args ++ " -pa " ++ Pa, - case (catch ?START_NODE(Name, A)) of + try ?START_NODE(Name, A) of {ok, Node} -> Node; - Else -> - ?line ?FAIL(Else) + {error, timeout} -> + e("Failed starting node ~p: timeout", [Name]), + ?line ?FAIL({error_starting_node, Name, timeout}); + {error, {already_running, Node}} when (Retry =:= true) -> + %% Ouch + %% Either we previously failed to (properly) stop the node + %% or it was a failed start, that reported failure (for instance + %% timeout) but actually succeeded. Regardless, we don't know + %% the state of this node, so (try) stop it and then (re-) try + %% start again. + e("Failed starting node ~p: Already Running - try stop", [Node]), + case ?STOP_NODE(Node) of + true -> + p("Successfully stopped old node ~p", [Node]), + start_node(Name, false); + false -> + e("Failed stop old node ~p", [Node]), + ?line ?FAIL({error_starting_node, Node, Retry, already_running}) + end; + {error, {already_running, Node}} -> + e("Failed starting node ~p: Already Running", [Node]), + ?line ?FAIL({error_starting_node, Node, Retry, already_running}); + {error, Reason} -> + e("Failed starting node ~p: ~p", [Name, Reason]), + ?line ?FAIL({error_starting_node, Name, Reason}) + catch + exit:{suite_failed, Reason} -> + e("(suite) Failed starting node ~p: ~p", [Name, Reason]), + ?line ?FAIL({failed_starting_node, Name, Reason}) end. -stop_node(Node) -> - rpc:cast(Node, erlang, halt, []), - await_stopped(Node, 5). -await_stopped(Node, 0) -> - p("await_stopped -> ~p still exist: giving up", [Node]), - ok; -await_stopped(Node, N) -> - Nodes = erlang:nodes(), - case lists:member(Node, Nodes) of - true -> - p("await_stopped -> ~p still exist: ~w", [Node, N]), - ?SLEEP(1000), - await_stopped(Node, N-1); - false -> - p("await_stopped -> ~p gone: ~w", [Node, N]), - ok +stop_node(Node) -> + case ?STOP_NODE(Node) of + true -> + ok; + false -> + ?line ?FAIL({failed_stop_node, Node}) end. @@ -6596,12 +6722,15 @@ rcall(Node, Mod, Func, Args) -> %% ------ +e(F, A) -> + p("<ERROR> " ++ F, A). + p(F) -> p(F, []). p(F, A) -> p(get(tname), F, A). - + p(TName, F, A) -> io:format("*** [~w][~s] ***" "~n " ++ F ++ "~n", [TName, formated_timestamp()|A]). diff --git a/lib/snmp/test/snmp_test_global_sys_monitor.erl b/lib/snmp/test/snmp_test_global_sys_monitor.erl new file mode 100644 index 0000000000..eafb96621a --- /dev/null +++ b/lib/snmp/test/snmp_test_global_sys_monitor.erl @@ -0,0 +1,214 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(snmp_test_global_sys_monitor). + +-export([start/0, stop/0, + reset_events/0, + events/0, + log/1]). +-export([init/1]). + +-define(NAME, ?MODULE). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start() -> + Parent = self(), + proc_lib:start(?MODULE, init, [Parent]). + +stop() -> + cast(stop). + +%% This does not reset the global counter but the "collector" +%% See events for more info. +reset_events() -> + cast(reset_events). + +events() -> + call(events). + +log(Event) -> + cast({node(), Event}). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init(Parent) -> + process_flag(priority, high), + case global:register_name(?NAME, self()) of + yes -> + info_msg("Starting", []), + proc_lib:init_ack(Parent, {ok, self()}), + loop(#{parent => Parent, ev_cnt => 0, evs => []}); + no -> + warning_msg("Already started", []), + proc_lib:init_ack(Parent, {error, already_started}), + exit(normal) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +loop(State) -> + receive + {?MODULE, stop} -> + warning_msg("Stopping with ~w events counted", + [maps:get(ev_cnt, State)]), + exit(normal); + + {?MODULE, reset_events} -> + loop(State#{evs => []}); + + {?MODULE, Ref, From, events} -> + Evs = maps:get(evs, State), + From ! {?MODULE, Ref, lists:reverse(Evs)}, + loop(State); + + {?MODULE, {Node, Event}} -> + State2 = process_event(State, Node, Event), + loop(State2); + + {nodedown = Event, Node} -> + State2 = process_event(State, Node, Event), + loop(State2); + + _ -> + loop(State) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +process_event(State, Node, {Pid, TS, Tag, Info}) -> + process_system_event(State, Node, Pid, TS, Tag, Info); + +process_event(State, Node, {TS, starting}) -> + FTS = snmp_misc:format_timestamp(TS), + info_msg("System Monitor on node ~p starting at ~s", [Node, FTS]), + if + (Node =/= node()) -> + erlang:monitor_node(Node, true); + true -> + ok + end, + State; + +process_event(State, Node, {TS, already_started}) -> + FTS = snmp_misc:format_timestamp(TS), + info_msg("System Monitor on node ~p already started", [Node, FTS]), + State; + +process_event(State, Node, nodedown) -> + info_msg("Node ~p down", [Node]), + State; + +process_event(State, Node, Event) -> + warning_msg("Received unknown event from node ~p:" + "~n ~p", [Node, Event]), + State. + + +%% System Monitor events +%% We only *count* system events +process_system_event(#{ev_cnt := Cnt, evs := Evs} = State, + Node, Pid, TS, long_gc = Ev, Info) -> + print_system_event("Long GC", Node, Pid, TS, Info), + State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]}; +process_system_event(#{ev_cnt := Cnt, evs := Evs} = State, + Node, Pid, TS, long_schedule = Ev, Info) -> + print_system_event("Long Schedule", Node, Pid, TS, Info), + State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]}; +process_system_event(#{ev_cnt := Cnt, evs := Evs} = State, + Node, Pid, TS, large_heap = Ev, Info) -> + print_system_event("Large Heap", Node, Pid, TS, Info), + State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]}; +process_system_event(#{ev_cnt := Cnt, evs := Evs} = State, + Node, Pid, TS, busy_port = Ev, Info) -> + print_system_event("Busy port", Node, Pid, TS, Info), + State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]}; +process_system_event(#{ev_cnt := Cnt, evs := Evs} = State, + Node, Pid, TS, busy_dist_port = Ev, Info) -> + print_system_event("Busy dist port", Node, Pid, TS, Info), + State#{ev_cnt => Cnt + 1, evs => [{Node, Ev} | Evs]}; + +%% And everything else +process_system_event(State, Node, Pid, TS, Tag, Info) -> + Pre = f("Unknown Event '~p'", [Tag]), + print_system_event(Pre, Node, Pid, TS, Info), + State. + + +print_system_event(Pre, Node, Pid, TS, Info) -> + FTS = snmp_misc:format_timestamp(TS), + warning_msg("~s from ~p (~p) at ~s:" + "~n ~p", [Pre, Node, Pid, FTS, Info]). + +f(F, A) -> + lists:flatten(io_lib:format(F, A)). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +cast(Msg) -> + try global:send(?NAME, {?MODULE, Msg}) of + Pid when is_pid(Pid) -> + ok + catch + C:E:_ -> + {error, {catched, C, E}} + end. + +call(Req) -> + call(Req, infinity). + +call(Req, Timeout) -> + Ref = make_ref(), + try global:send(?NAME, {?MODULE, Ref, self(), Req}) of + Pid when is_pid(Pid) -> + receive + {?MODULE, Ref, Rep} -> + Rep + after Timeout -> + {error, timeout} + end + catch + C:E:_ -> + {error, {catched, C, E}} + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +info_msg(F, A) -> + error_logger:info_msg(format_msg(F, A), []). + +warning_msg(F, A) -> + error_logger:warning_msg(format_msg(F, A), []). + + +format_msg(F, A) -> + f("~n" ++ + "****** SNMP TEST GLOBAL SYSTEM MONITOR ******~n~n" ++ + F ++ + "~n~n", + A). + diff --git a/lib/snmp/test/snmp_test_lib.erl b/lib/snmp/test/snmp_test_lib.erl index a483690653..26b68501e2 100644 --- a/lib/snmp/test/snmp_test_lib.erl +++ b/lib/snmp/test/snmp_test_lib.erl @@ -23,9 +23,12 @@ -include_lib("kernel/include/file.hrl"). +-export([tc_try/2, tc_try/3]). -export([hostname/0, hostname/1, localhost/0, localhost/1, os_type/0, sz/1, display_suite_info/1]). --export([non_pc_tc_maybe_skip/4, os_based_skip/1]). +-export([non_pc_tc_maybe_skip/4, os_based_skip/1, + has_support_ipv6/0, has_support_ipv6/1, + is_ipv6_host/0, is_ipv6_host/1]). -export([fix_data_dir/1, init_suite_top_dir/2, init_group_top_dir/2, init_testcase_top_dir/2, lookup/2, @@ -34,17 +37,114 @@ -export([hours/1, minutes/1, seconds/1, sleep/1]). -export([flush_mqueue/0, trap_exit/0, trap_exit/1]). -export([ping/1, local_nodes/0, nodes_on/1]). --export([start_node/2]). +-export([start_node/2, stop_node/1]). -export([is_app_running/1, is_crypto_running/0, is_mnesia_running/0, is_snmp_running/0]). -export([crypto_start/0, crypto_support/0]). -export([watchdog/3, watchdog_start/1, watchdog_start/2, watchdog_stop/1]). -export([del_dir/1]). -export([cover/1]). --export([p/2, print1/2, print2/2, print/5, formated_timestamp/0]). +-export([f/2, p/2, print1/2, print2/2, print/5, formated_timestamp/0]). %% ---------------------------------------------------------------------- +%% Run test-case +%% + +%% *** tc_try/2,3 *** +%% Case: Basically the test case name +%% TCCondFun: A fun that is evaluated before the actual test case +%% The point of this is that it can performs checks to +%% see if we shall run the test case at all. +%% For instance, the test case may only work in specific +%% conditions. +%% FCFun: The test case fun +tc_try(Case, TCFun) -> + tc_try(Case, fun() -> ok end, TCFun). + +tc_try(Case, TCCondFun, TCFun) + when is_atom(Case) andalso + is_function(TCCondFun, 0) andalso + is_function(TCFun, 0) -> + tc_begin(Case), + try TCCondFun() of + ok -> + try + begin + TCFun(), + sleep(seconds(1)), + tc_end("ok") + end + catch + C:{skip, _} = SKIP when ((C =:= throw) orelse (C =:= exit)) -> + tc_end( f("skipping(catched,~w,tc)", [C]) ), + SKIP; + C:E:S -> + tc_end( f("failed(catched,~w,tc)", [C]) ), + erlang:raise(C, E, S) + end; + {skip, _} = SKIP -> + tc_end("skipping(tc)"), + SKIP; + {error, Reason} -> + tc_end("failed(tc)"), + exit({tc_cond_failed, Reason}) + catch + C:{skip, _} = SKIP when ((C =:= throw) orelse (C =:= exit)) -> + tc_end( f("skipping(catched,~w,cond)", [C]) ), + SKIP; + C:E:S -> + tc_end( f("failed(catched,~w,cond)", [C]) ), + erlang:raise(C, E, S) + end. + + +tc_set_name(N) when is_atom(N) -> + tc_set_name(atom_to_list(N)); +tc_set_name(N) when is_list(N) -> + put(tc_name, N). + +tc_get_name() -> + get(tc_name). + +tc_begin(TC) -> + OldVal = process_flag(trap_exit, true), + put(old_trap_exit, OldVal), + tc_set_name(TC), + tc_print("begin ***", + "~n----------------------------------------------------~n", ""). + +tc_end(Result) when is_list(Result) -> + OldVal = erase(old_trap_exit), + process_flag(trap_exit, OldVal), + tc_print("done: ~s", [Result], + "", "----------------------------------------------------~n~n"), + ok. + +tc_print(F, Before, After) -> + tc_print(F, [], Before, After). + +tc_print(F, A, Before, After) -> + Name = tc_which_name(), + FStr = f("*** [~s][~s][~p] " ++ F ++ "~n", + [formated_timestamp(),Name,self()|A]), + io:format(user, Before ++ FStr ++ After, []). + +tc_which_name() -> + case tc_get_name() of + undefined -> + case get(sname) of + undefined -> + ""; + SName when is_list(SName) -> + SName + end; + Name when is_list(Name) -> + Name + end. + + +%% ---------------------------------------------------------------------- %% Misc functions %% @@ -52,11 +152,12 @@ hostname() -> hostname(node()). hostname(Node) -> - from($@, atom_to_list(Node)). - -from(H, [H | T]) -> T; -from(H, [_ | T]) -> from(H, T); -from(_H, []) -> []. + case string:tokens(atom_to_list(Node), [$@]) of + [_, Host] -> + Host; + _ -> + [] + end. %% localhost() -> %% {ok, Ip} = snmp_misc:ip(net_adm:localhost()), @@ -78,7 +179,9 @@ localhost(Family) -> {error, Reason1} -> fail({getifaddrs, Reason1}, ?MODULE, ?LINE) end; - {ok, {0, _, _, _, _, _, _, _}} when (Family =:= inet6) -> + {ok, {A1, _, _, _, _, _, _, _}} when (Family =:= inet6) andalso + ((A1 =:= 0) orelse + (A1 =:= 16#fe80)) -> %% Ouch, we need to use something else case inet:getifaddrs() of {ok, IfList} -> @@ -197,63 +300,121 @@ non_pc_tc_maybe_skip(Config, Condition, File, Line) %% test-server... ok; _ -> - case Condition() of + try Condition() of true -> skip(non_pc_testcase, File, Line); false -> ok + catch + C:E:S -> + skip({condition, C, E, S}, File, Line) end end end. +%% The type and spec'ing is just to increase readability +-type os_family() :: win32 | unix. +-type os_name() :: atom(). +-type os_version() :: string() | {non_neg_integer(), + non_neg_integer(), + non_neg_integer()}. +-type os_skip_check() :: fun(() -> boolean()) | + fun((os_version()) -> boolean()). +-type skippable() :: any | [os_family() | + {os_family(), os_name() | + [os_name() | {os_name(), + os_skip_check()}]}]. + +-spec os_based_skip(skippable()) -> boolean(). + os_based_skip(any) -> - io:format("os_based_skip(any) -> entry" - "~n", []), true; os_based_skip(Skippable) when is_list(Skippable) -> - io:format("os_based_skip -> entry with" - "~n Skippable: ~p" - "~n", [Skippable]), - {OsFam, OsName} = - case os:type() of - {_Fam, _Name} = FamAndName -> - FamAndName; - Fam -> - {Fam, undefined} - end, - io:format("os_based_skip -> os-type: " - "~n OsFam: ~p" - "~n OsName: ~p" - "~n", [OsFam, OsName]), + os_base_skip(Skippable, os:type()); +os_based_skip(_Crap) -> + false. + +os_base_skip(Skippable, {OsFam, OsName}) -> + os_base_skip(Skippable, OsFam, OsName); +os_base_skip(Skippable, OsFam) -> + os_base_skip(Skippable, OsFam, undefined). + +os_base_skip(Skippable, OsFam, OsName) -> + %% Check if the entire family is to be skipped + %% Example: [win32, unix] case lists:member(OsFam, Skippable) of true -> true; false -> - case lists:keysearch(OsFam, 1, Skippable) of - {value, {OsFam, OsName}} -> - true; - {value, {OsFam, OsNames}} when is_list(OsNames) -> + %% Example: [{unix, freebsd}] | [{unix, [freebsd, darwin]}] + case lists:keysearch(OsFam, 1, Skippable) of + {value, {OsFam, OsName}} -> + true; + {value, {OsFam, OsNames}} when is_list(OsNames) -> + %% OsNames is a list of: + %% [atom()|{atom(), function/0 | function/1}] case lists:member(OsName, OsNames) of true -> true; false -> - case lists:keymember(OsName, 1, OsNames) of - {value, {OsName, Check}} when is_function(Check) -> - Check(); - _ -> - false - end + os_based_skip_check(OsName, OsNames) end; - _ -> - false - end - end; -os_based_skip(_Crap) -> - io:format("os_based_skip -> entry with" - "~n _Crap: ~p" - "~n", [_Crap]), - false. + _ -> + false + end + end. + +%% Performs a check via a provided fun with arity 0 or 1. +%% The argument is the result of os:version(). +os_based_skip_check(OsName, OsNames) -> + case lists:keysearch(OsName, 1, OsNames) of + {value, {OsName, Check}} when is_function(Check, 0) -> + Check(); + {value, {OsName, Check}} when is_function(Check, 1) -> + Check(os:version()); + _ -> + false + end. + + +%% A basic test to check if current host supports IPv6 +has_support_ipv6() -> + case inet:gethostname() of + {ok, Hostname} -> + has_support_ipv6(Hostname); + _ -> + false + end. + +has_support_ipv6(Hostname) -> + case inet:getaddr(Hostname, inet6) of + {ok, Addr} when (size(Addr) =:= 8) andalso + (element(1, Addr) =/= 0) andalso + (element(1, Addr) =/= 16#fe80) -> + true; + {ok, _} -> + false; + {error, _} -> + false + end. + + +is_ipv6_host() -> + case inet:gethostname() of + {ok, Hostname} -> + is_ipv6_host(Hostname); + {error, _} -> + false + end. + +is_ipv6_host(Hostname) -> + case ct:require(ipv6_hosts) of + ok -> + lists:member(list_to_atom(Hostname), ct:get_config(ipv6_hosts)); + _ -> + false + end. %% ---------------------------------------------------------------- @@ -452,10 +613,14 @@ nodes_on(Host) when is_list(Host) -> start_node(Name, Args) -> - Opts = [{cleanup,false}, {args,Args}], + Opts = [{cleanup, false}, {args, Args}], test_server:start_node(Name, slave, Opts). +stop_node(Node) -> + test_server:stop_node(Node). + + %% ---------------------------------------------------------------- %% Application and Crypto utility functions %% @@ -648,6 +813,9 @@ cover([Suite, Case] = Args) when is_atom(Suite) andalso is_atom(Case) -> %% (debug) Print functions %% +f(F, A) -> + lists:flatten(io_lib:format(F, A)). + p(Mod, Case) when is_atom(Mod) andalso is_atom(Case) -> case get(test_case) of undefined -> diff --git a/lib/snmp/test/snmp_test_lib.hrl b/lib/snmp/test/snmp_test_lib.hrl index 335f3fff3c..f077f15d3e 100644 --- a/lib/snmp/test/snmp_test_lib.hrl +++ b/lib/snmp/test/snmp_test_lib.hrl @@ -18,15 +18,11 @@ %% The Initial Developer of the Original Code is Ericsson AB. %% %CopyrightEnd% %% + %%---------------------------------------------------------------------- -%% Purpose: Define common macros for testing +%% Purpose: Define common macros for (the snmp) testing %%---------------------------------------------------------------------- -%% - (some of the) Macros stolen from the test server - - -%% -define(line,put(test_server_loc,{?MODULE,?LINE}),). - - %% - Misc macros - -ifndef(APPLICATION). @@ -45,12 +41,18 @@ %% - Test case macros - +-define(TC_TRY(C, TC), snmp_test_lib:tc_try(C, TC)). +-define(TC_TRY(C, TCCond, TC), snmp_test_lib:tc_try(C, TCCond, TC)). -define(OS_BASED_SKIP(Skippable), snmp_test_lib:os_based_skip(Skippable)). -define(NON_PC_TC_MAYBE_SKIP(Config, Condition), snmp_test_lib:non_pc_tc_maybe_skip(Config, Condition, ?MODULE, ?LINE)). --define(SKIP(Reason), snmp_test_lib:skip(Reason, ?MODULE, ?LINE)). --define(FAIL(Reason), snmp_test_lib:fail(Reason, ?MODULE, ?LINE)). +-define(SKIP(Reason), snmp_test_lib:skip(Reason, ?MODULE, ?LINE)). +-define(FAIL(Reason), snmp_test_lib:fail(Reason, ?MODULE, ?LINE)). +-define(IS_IPV6_HOST(), snmp_test_lib:is_ipv6_host()). +-define(IS_IPV6_HOST(H), snmp_test_lib:is_ipv6_host(H)). +-define(HAS_SUPPORT_IPV6(), snmp_test_lib:has_support_ipv6()). +-define(HAS_SUPPORT_IPV6(H), snmp_test_lib:has_support_ipv6(H)). %% - Time macros - @@ -88,6 +90,7 @@ -define(LNODES(), snmp_test_lib:local_nodes()). -define(NODES(H), snmp_test_lib:nodes_on(H)). -define(START_NODE(N,A), snmp_test_lib:start_node(N,A)). +-define(STOP_NODE(N), snmp_test_lib:stop_node(N)). %% - Application and Crypto utility macros - @@ -150,8 +153,10 @@ snmp_test_lib:print(P, ?MODULE, ?LINE, F, A)). -define(PRINT1(F, A), snmp_test_lib:print1(F, A)). +-define(PRINT1(F), ?PRINT1(F, [])). -define(EPRINT1(F, A), ?PRINT1("<ERROR> " ++ F, A)). -define(PRINT2(F, A), snmp_test_lib:print2(F, A)). +-define(PRINT2(F), ?PRINT2(F, [])). -define(EPRINT2(F, A), ?PRINT2("<ERROR> " ++ F, A)). diff --git a/lib/snmp/test/snmp_test_mgr.erl b/lib/snmp/test/snmp_test_mgr.erl index 73a4d56084..9d6be65088 100644 --- a/lib/snmp/test/snmp_test_mgr.erl +++ b/lib/snmp/test/snmp_test_mgr.erl @@ -52,6 +52,7 @@ -include_lib("snmp/include/snmp_types.hrl"). -include_lib("snmp/include/STANDARD-MIB.hrl"). -include("snmp_test_lib.hrl"). +-include_lib("snmp/src/app/snmp_internal.hrl"). -record(state, {dbg = true, quiet, @@ -192,9 +193,11 @@ receive_trap(Timeout) -> init({Options, CallerPid}) -> put(sname, mgr), put(verbosity, debug), - random:seed(erlang:phash2([node()]), - erlang:monotonic_time(), - erlang:unique_integer()), + ?SNMP_RAND_SEED(), + %% rand:seed(exrop, + %% {erlang:phash2([node()]), + %% erlang:monotonic_time(), + %% erlang:unique_integer()}), case (catch is_options_ok(Options)) of true -> put(debug, get_value(debug, Options, false)), @@ -244,10 +247,21 @@ init({Options, CallerPid}) -> IpFamily = get_value(ipfamily, Options, inet), print("[~w] ~p -> IpFamily: ~p~n", [?MODULE, self(), IpFamily]), AgIp = case snmp_misc:assq(agent, Options) of - {value, Tuple4} when is_tuple(Tuple4) andalso - (size(Tuple4) =:= 4) -> - Tuple4; + {value, Addr} when is_tuple(Addr) andalso + (size(Addr) =:= 4) andalso + (IpFamily =:= inet) -> + print("[~w] ~p -> Addr: ~p~n", + [?MODULE, self(), Addr]), + Addr; + {value, Addr} when is_tuple(Addr) andalso + (size(Addr) =:= 8) andalso + (IpFamily =:= inet6) -> + print("[~w] ~p -> Addr: ~p~n", + [?MODULE, self(), Addr]), + Addr; {value, Host} when is_list(Host) -> + print("[~w] ~p -> Host: ~p~n", + [?MODULE, self(), Host]), {ok, Ip} = snmp_misc:ip(Host, IpFamily), Ip end, @@ -668,7 +682,6 @@ make_vb(Oid) -> #varbind{oid = Oid, variabletype = 'NULL', value = 'NULL'}. make_request_id() -> - %% random:uniform(16#FFFFFFF-1). snmp_test_mgr_counter_server:increment(mgr_request_id, 1, 1, 2147483647). echo_pdu(PDU, MiniMIB) -> @@ -1141,5 +1154,5 @@ d(_,_F,_A) -> print(F, A) -> ?PRINT2("MGR " ++ F, A). -formated_timestamp() -> - snmp_test_lib:formated_timestamp(). +%% formated_timestamp() -> +%% snmp_test_lib:formated_timestamp(). diff --git a/lib/snmp/test/snmp_test_mgr_misc.erl b/lib/snmp/test/snmp_test_mgr_misc.erl index 315e3ebd9e..6608a88c00 100644 --- a/lib/snmp/test/snmp_test_mgr_misc.erl +++ b/lib/snmp/test/snmp_test_mgr_misc.erl @@ -576,6 +576,7 @@ vsn('version-2') -> v2c. udp_send(UdpId, AgentIp, UdpPort, B) -> + ?vlog("attempt send message (~w bytes) to ~p", [sz(B), {AgentIp, UdpPort}]), case (catch gen_udp:send(UdpId, AgentIp, UdpPort, B)) of {error,ErrorReason} -> error("failed (error) sending message to ~p:~p: " @@ -880,7 +881,7 @@ display_prop_hdr(S) -> %%---------------------------------------------------------------------- sz(L) when is_list(L) -> - length(lists:flatten(L)); + iolist_size(L); sz(B) when is_binary(B) -> size(B); sz(O) -> diff --git a/lib/snmp/test/snmp_test_server.erl b/lib/snmp/test/snmp_test_server.erl index a77bdc142c..ab7dbbbaa0 100644 --- a/lib/snmp/test/snmp_test_server.erl +++ b/lib/snmp/test/snmp_test_server.erl @@ -207,7 +207,7 @@ do_subcases(Mod, Fun, [{conf, Init, Cases, Finish}|SubCases], Config, Acc) [{failed, {Mod, Fun}, Error}] end, do_subcases(Mod, Fun, SubCases, Config, [R|Acc]); -do_subcases(Mod, Fun, [SubCase|SubCases], Config, Acc) when atom(SubCase) -> +do_subcases(Mod, Fun, [SubCase|SubCases], Config, Acc) when is_atom(SubCase) -> R = do_case(Mod, SubCase, Config), do_subcases(Mod, Fun, SubCases,Config, [R|Acc]). @@ -407,7 +407,7 @@ d(_, _, _, _) -> ok. timestamp() -> - {Date, Time} = calendar:now_to_datetime( now() ), + {Date, Time} = calendar:now_to_datetime( erlang:timestamp() ), {YYYY, MM, DD} = Date, {Hour, Min, Sec} = Time, FormatDate = diff --git a/lib/snmp/test/snmp_test_sys_monitor.erl b/lib/snmp/test/snmp_test_sys_monitor.erl new file mode 100644 index 0000000000..2291c6ca97 --- /dev/null +++ b/lib/snmp/test/snmp_test_sys_monitor.erl @@ -0,0 +1,86 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(snmp_test_sys_monitor). + +-export([start/0, stop/0, + init/1]). + +-define(NAME, ?MODULE). +-define(GSM, snmp_test_global_sys_monitor). + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +start() -> + Parent = self(), + proc_lib:start(?MODULE, init, [Parent]). + +stop() -> + case whereis(?NAME) of + Pid when is_pid(Pid) -> + Pid ! {?MODULE, stop}, + ok; + _ -> + ok + end. + + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +init(Parent) -> + process_flag(priority, high), + try register(?NAME, self()) of + true -> + global:sync(), + MonSettings = [ + busy_port, + busy_dist_port, + {long_gc, 1000}, + {long_schedule, 1000}, + {large_heap, 8*1024*1024} % 8 MB + ], + erlang:system_monitor(self(), MonSettings), + ?GSM:log({erlang:timestamp(), starting}), + proc_lib:init_ack(Parent, {ok, self()}), + loop(#{parent => Parent}) + catch + _:_:_ -> + ?GSM:log({erlang:timestamp(), already_started}), + proc_lib:init_ack(Parent, {error, already_started}), + exit(normal) + end. + + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +loop(State) -> + receive + {monitor, Pid, Tag, Info} -> + ?GSM:log({Pid, erlang:timestamp(), Tag, Info}), + loop(State); + + _ -> + loop(State) + end. + + + diff --git a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl index b83c7461d1..b8bdb30271 100644 --- a/lib/snmp/test/snmp_to_snmpnet_SUITE.erl +++ b/lib/snmp/test/snmp_to_snmpnet_SUITE.erl @@ -247,9 +247,17 @@ erlang_agent_netsnmp_get(Config) when is_list(Config) -> start_agent(Config), Oid = ?sysDescr_instance, Expected = expected(Oid, get), - [Expected = snmpget(Oid, Transport, Config) - || Transport <- Transports], - ok. + try + begin + [Expected = snmpget(Oid, Transport, Config) + || Transport <- Transports], + ok + end + catch + throw:{skip, _} = SKIP -> + SKIP + end. + %%-------------------------------------------------------------------- erlang_manager_netsnmp_get() -> @@ -260,29 +268,34 @@ erlang_manager_netsnmp_get(Config) when is_list(Config) -> SysDescr = "Net-SNMP agent", TargetName = "Target Net-SNMP agent", Transports = ?config(transports, Config), - ProgHandle = start_snmpd(Community, SysDescr, Config), - start_manager(Config), - snmp_manager_user:start_link(self(), test_user), - [snmp_manager_user:register_agent( - TargetName++domain_suffix(Domain), - [{reg_type, target_name}, - {tdomain, Domain}, {taddress, Addr}, - {community, Community}, {engine_id, "EngineId"}, - {version, v2}, {sec_model, v2c}, {sec_level, noAuthNoPriv}]) - || {Domain, Addr} <- Transports], - Results = - [snmp_manager_user:sync_get( - TargetName++domain_suffix(Domain), - [?sysDescr_instance]) - || {Domain, _} <- Transports], - ct:pal("sync_get -> ~p", [Results]), - snmp_manager_user:stop(), - stop_program(ProgHandle), - [{ok, - {noError, 0, - [{varbind, ?sysDescr_instance, 'OCTET STRING', SysDescr,1}] }, - _} = R || R <- Results], - ok. + case start_snmpd(Community, SysDescr, Config) of + {skip, _} = SKIP -> + SKIP; + ProgHandle -> + start_manager(Config), + snmp_manager_user:start_link(self(), test_user), + [snmp_manager_user:register_agent( + TargetName++domain_suffix(Domain), + [{reg_type, target_name}, + {tdomain, Domain}, {taddress, Addr}, + {community, Community}, {engine_id, "EngineId"}, + {version, v2}, {sec_model, v2c}, {sec_level, noAuthNoPriv}]) + || {Domain, Addr} <- Transports], + Results = + [snmp_manager_user:sync_get( + TargetName++domain_suffix(Domain), + [?sysDescr_instance]) + || {Domain, _} <- Transports], + ct:pal("sync_get -> ~p", [Results]), + snmp_manager_user:stop(), + stop_program(ProgHandle), + [{ok, + {noError, 0, + [{varbind, ?sysDescr_instance, 'OCTET STRING', SysDescr,1}] }, + _} = R || R <- Results], + ok + end. + %%-------------------------------------------------------------------- erlang_agent_netsnmp_inform(Config) when is_list(Config) -> @@ -292,17 +305,19 @@ erlang_agent_netsnmp_inform(Config) when is_list(Config) -> start_agent(Config), ok = snmpa:load_mib(snmp_master_agent, filename:join(DataDir, Mib)), - ProgHandle = start_snmptrapd(Mib, Config), - - snmpa:send_notification( - snmp_master_agent, testTrapv22, {erlang_agent_test, self()}), - - receive - {snmp_targets, erlang_agent_test, Addresses} -> - ct:pal("Notification sent to: ~p~n", [Addresses]), - erlang_agent_netsnmp_inform_responses(Addresses) - end, - stop_program(ProgHandle). + case start_snmptrapd(Mib, Config) of + {skip, _} = SKIP -> + SKIP; + ProgHandle -> + snmpa:send_notification( + snmp_master_agent, testTrapv22, {erlang_agent_test, self()}), + receive + {snmp_targets, erlang_agent_test, Addresses} -> + ct:pal("Notification sent to: ~p~n", [Addresses]), + erlang_agent_netsnmp_inform_responses(Addresses) + end, + stop_program(ProgHandle) + end. erlang_agent_netsnmp_inform_responses([]) -> receive @@ -326,6 +341,7 @@ erlang_agent_netsnmp_inform_responses([Address | Addresses]) -> %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- + snmpget(Oid, Transport, Config) -> Versions = ?config(snmp_versions, Config), @@ -335,10 +351,14 @@ snmpget(Oid, Transport, Config) -> "-Cf", net_snmp_addr_str(Transport), oid_str(Oid)], - ProgHandle = start_program(snmpget, Args, none, Config), - {_, line, Line} = get_program_output(ProgHandle), - stop_program(ProgHandle), - Line. + case start_program(snmpget, Args, none, Config) of + {skip, _} = SKIP -> + throw(SKIP); + ProgHandle -> + {_, line, Line} = get_program_output(ProgHandle), + stop_program(ProgHandle), + Line + end. start_snmptrapd(Mibs, Config) -> DataDir = ?config(data_dir, Config), @@ -382,12 +402,13 @@ start_program(Prog, Args, StartCheckMP, Config) -> DataDir = ?config(data_dir, Config), StartWrapper = filename:join(DataDir, "start_stop_wrapper"), Parent = self(), - Pid = - spawn_link( + %% process_flag(trap_exit, true), + {Pid, Mon} = + spawn_monitor( fun () -> run_program(Parent, StartWrapper, [Path | Args]) end), - start_check(Pid, erlang:monitor(process, Pid), StartCheckMP). + start_check(Pid, Mon, StartCheckMP). start_check(Pid, Mon, none) -> {Pid, Mon}; @@ -400,6 +421,10 @@ start_check(Pid, Mon, StartCheckMP) -> nomatch -> start_check(Pid, Mon, StartCheckMP) end; + {'DOWN', Mon, _, _Pid, {skip, Reason} = SKIP} -> + ct:pal("Received DOWN from ~p" + "~n Skip Reason: ~p", [_Pid, Reason]), + SKIP; {'DOWN', Mon, _, _, Reason} -> ct:fail("Prog ~p start failed: ~p", [Pid, Reason]) end. @@ -446,14 +471,34 @@ run_program_loop(Parent, Port, Buf) -> {Port, {data, {Flag, Data}}} -> case Flag of eol -> - Line = iolist_to_binary(lists:reverse(Buf, Data)), - ct:pal("Prog ~p output: ~s", [Port, Line]), - Parent ! {self(), line, Line}, - run_program_loop(Parent, Port, []); + Line = iolist_to_binary(lists:reverse(Buf, Data)), + ct:pal("Prog ~p output: ~s", [Port, Line]), + %% There are potentially many different fail outputs, + %% but for now we test for just this one: illegal option + IOpt = "illegal option", + case string:find(binary_to_list(Line), IOpt) of + nomatch -> + Parent ! {self(), line, Line}, + run_program_loop(Parent, Port, []); + Line2 -> + %% Try to extract the actual illegal option string + IOpt2 = + case string:take( + string:prefix(Line2, IOpt), [$-, $ ]) of + {_, Str} when length(Str) > 0 -> + Str; + _X -> + Line2 + end, + ct:pal("Force program ~p stop", [Port]), + true = port_command(Port, <<"stop\n">>), + (catch port_close(Port)), + exit({skip, {illegal_option, IOpt2}}) + end; noeol -> run_program_loop(Parent, Port, [Data | Buf]) end; - {Port, {exit_status,ExitStatus}} -> + {Port, {exit_status, ExitStatus}} -> ct:pal("Prog ~p exit: ~p", [Port, ExitStatus]), catch port_close(Port), Parent ! {self(), exit, ExitStatus}; diff --git a/lib/ssh/doc/src/ssh.xml b/lib/ssh/doc/src/ssh.xml index 3fd6eae423..8b7cb4dcd4 100644 --- a/lib/ssh/doc/src/ssh.xml +++ b/lib/ssh/doc/src/ssh.xml @@ -347,6 +347,7 @@ <datatype> <name name="subsystem_daemon_option"/> + <name name="subsystem_specs"/> <name name="subsystem_spec"/> <desc> <p>Defines a subsystem in the daemon.</p> diff --git a/lib/ssh/src/Makefile b/lib/ssh/src/Makefile index 6d64a45112..9627b70eeb 100644 --- a/lib/ssh/src/Makefile +++ b/lib/ssh/src/Makefile @@ -99,7 +99,7 @@ APP_TARGET= $(EBIN)/$(APP_FILE) APPUP_SRC= $(APPUP_FILE).src APPUP_TARGET= $(EBIN)/$(APPUP_FILE) -INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl ssh.hrl ssh_userauth.hrl ssh_xfer.hrl +INTERNAL_HRL_FILES = ssh_auth.hrl ssh_connect.hrl ssh_transport.hrl ssh.hrl ssh_xfer.hrl # ---------------------------------------------------- # FLAGS diff --git a/lib/ssh/src/ssh.hrl b/lib/ssh/src/ssh.hrl index 04453e6ef0..a991f72cf2 100644 --- a/lib/ssh/src/ssh.hrl +++ b/lib/ssh/src/ssh.hrl @@ -68,6 +68,25 @@ -define(string(X), ?string_utf8(X)). -define(binary(X), << ?STRING(X) >>). +-define('2bin'(X), (if is_binary(X) -> X; + is_list(X) -> list_to_binary(X); + X==undefined -> <<>> + end) ). + +%% encoding macros +-define('E...'(X), ?'2bin'(X)/binary ). +-define(Eboolean(X), ?BOOLEAN(case X of + true -> ?TRUE; + false -> ?FALSE + end) ). +-define(Ebyte(X), ?BYTE(X) ). +-define(Euint32(X), ?UINT32(X) ). +-define(Estring(X), ?STRING(?'2bin'(X)) ). +-define(Estring_utf8(X), ?string_utf8(X)/binary ). +-define(Ename_list(X), ?STRING(ssh_bits:name_list(X)) ). +-define(Empint(X), (ssh_bits:mpint(X))/binary ). +-define(Ebinary(X), ?STRING(X) ). + %% Cipher details -define(SSH_CIPHER_NONE, 0). -define(SSH_CIPHER_3DES, 3). @@ -293,7 +312,8 @@ | gen_tcp:listen_option() | ?COMMON_OPTION . --type subsystem_daemon_option() :: {subsystems, subsystem_spec()}. +-type subsystem_daemon_option() :: {subsystems, subsystem_specs()}. +-type subsystem_specs() :: [ subsystem_spec() ]. -type shell_daemon_option() :: {shell, mod_fun_args() | 'shell_fun/1'() | 'shell_fun/2'() }. -type 'shell_fun/1'() :: fun((User::string()) -> pid()) . @@ -459,14 +479,6 @@ recv_ext_info }). --record(ssh_key, - { - type, - public, - private, - comment = "" - }). - -record(ssh_pty, {term = "", % e.g. "xterm" width = 80, height = 25, @@ -474,13 +486,6 @@ pixel_height = 768, modes = <<>>}). -%% assertion macro --define(ssh_assert(Expr, Reason), - case Expr of - true -> ok; - _ -> exit(Reason) - end). - %% dbg help macros -define(wr_record(N,BlackList), diff --git a/lib/ssh/src/ssh_connection_handler.erl b/lib/ssh/src/ssh_connection_handler.erl index 8f32966a12..9df4f1e2d7 100644 --- a/lib/ssh/src/ssh_connection_handler.erl +++ b/lib/ssh/src/ssh_connection_handler.erl @@ -386,16 +386,24 @@ init_connection_handler(Role, Socket, Opts) -> D); {stop, Error} -> - Sups = ?GET_INTERNAL_OPT(supervisors, Opts), - C = #connection{system_supervisor = proplists:get_value(system_sup, Sups), - sub_system_supervisor = proplists:get_value(subsystem_sup, Sups), - connection_supervisor = proplists:get_value(connection_sup, Sups) - }, + D = try + %% Only servers have supervisorts defined in Opts + Sups = ?GET_INTERNAL_OPT(supervisors, Opts), + #connection{system_supervisor = proplists:get_value(system_sup, Sups), + sub_system_supervisor = proplists:get_value(subsystem_sup, Sups), + connection_supervisor = proplists:get_value(connection_sup, Sups) + } + of + C -> + #data{connection_state=C} + catch + _:_ -> + #data{connection_state=#connection{}} + end, gen_statem:enter_loop(?MODULE, [], {init_error,Error}, - #data{connection_state=C, - socket=Socket}) + D#data{socket=Socket}) end. @@ -1550,7 +1558,7 @@ terminate({shutdown,"Connection closed"}, _StateName, D) -> terminate({shutdown,{init,Reason}}, StateName, D) -> %% Error in initiation. "This error should not occur". - log(error, D, io_lib:format("Shutdown in init (StateName=~p): ~p~n",[StateName,Reason])), + log(error, D, "Shutdown in init (StateName=~p): ~p~n", [StateName,Reason]), stop_subsystem(D), close_transport(D); @@ -1952,12 +1960,12 @@ send_disconnect(Code, Reason, DetailedText, Module, Line, StateName, D0) -> call_disconnectfun_and_log_cond(LogMsg, DetailedText, Module, Line, StateName, D) -> case disconnect_fun(LogMsg, D) of void -> - log(info, D, - io_lib:format("~s~n" - "State = ~p~n" - "Module = ~p, Line = ~p.~n" - "Details:~n ~s~n", - [LogMsg, StateName, Module, Line, DetailedText])); + log(info, D, + "~s~n" + "State = ~p~n" + "Module = ~p, Line = ~p.~n" + "Details:~n ~s~n", + [LogMsg, StateName, Module, Line, DetailedText]); _ -> ok end. @@ -2021,6 +2029,9 @@ fold_keys(Keys, Fun, Extra) -> end, [], Keys). %%%---------------------------------------------------------------- +log(Tag, D, Format, Args) -> + log(Tag, D, io_lib:format(Format,Args)). + log(Tag, D, Reason) -> case atom_to_list(Tag) of % Dialyzer-technical reasons... "error" -> do_log(error_msg, Reason, D); @@ -2028,36 +2039,50 @@ log(Tag, D, Reason) -> "info" -> do_log(info_msg, Reason, D) end. -do_log(F, Reason, #data{ssh_params = #ssh{role = Role} = S - }) -> - VSN = - case application:get_key(ssh,vsn) of - {ok,Vsn} -> Vsn; - undefined -> "" - end, - PeerVersion = - case Role of - server -> S#ssh.c_version; - client -> S#ssh.s_version - end, - CryptoInfo = - try - [{_,_,CI}] = crypto:info_lib(), - <<"(",CI/binary,")">> - catch - _:_ -> "" - end, - Other = - case Role of - server -> "Client"; - client -> "Server" - end, - error_logger:F("Erlang SSH ~p ~s ~s.~n" - "~s: ~p~n" - "~s~n", - [Role, VSN, CryptoInfo, - Other, PeerVersion, - Reason]). + +do_log(F, Reason, #data{ssh_params = S}) -> + case S of + #ssh{role = Role} when Role==server ; + Role==client -> + {PeerRole,PeerVersion} = + case Role of + server -> {"Client", S#ssh.c_version}; + client -> {"Server", S#ssh.s_version} + end, + error_logger:F("Erlang SSH ~p ~s ~s.~n" + "~s: ~p~n" + "~s~n", + [Role, + ssh_log_version(), crypto_log_info(), + PeerRole, PeerVersion, + Reason]); + _ -> + error_logger:F("Erlang SSH ~s ~s.~n" + "~s~n", + [ssh_log_version(), crypto_log_info(), + Reason]) + end. + +crypto_log_info() -> + try + [{_,_,CI}] = crypto:info_lib(), + case crypto:info_fips() of + enabled -> + <<"(",CI/binary,". FIPS enabled)">>; + not_enabled -> + <<"(",CI/binary,". FIPS available but not enabled)">>; + _ -> + <<"(",CI/binary,")">> + end + catch + _:_ -> "" + end. + +ssh_log_version() -> + case application:get_key(ssh,vsn) of + {ok,Vsn} -> Vsn; + undefined -> "" + end. %%%---------------------------------------------------------------- not_connected_filter({connection_reply, _Data}) -> true; diff --git a/lib/ssh/src/ssh_message.erl b/lib/ssh/src/ssh_message.erl index d95e58c1bb..7c86a81108 100644 --- a/lib/ssh/src/ssh_message.erl +++ b/lib/ssh/src/ssh_message.erl @@ -34,24 +34,6 @@ -export([dbg_trace/3]). --define('2bin'(X), (if is_binary(X) -> X; - is_list(X) -> list_to_binary(X); - X==undefined -> <<>> - end) ). - --define('E...'(X), ?'2bin'(X)/binary ). --define(Eboolean(X), ?BOOLEAN(case X of - true -> ?TRUE; - false -> ?FALSE - end) ). --define(Ebyte(X), ?BYTE(X) ). --define(Euint32(X), ?UINT32(X) ). --define(Estring(X), ?STRING(?'2bin'(X)) ). --define(Estring_utf8(X), ?string_utf8(X)/binary ). --define(Ename_list(X), ?STRING(ssh_bits:name_list(X)) ). --define(Empint(X), (ssh_bits:mpint(X))/binary ). --define(Ebinary(X), ?STRING(X) ). - ucl(B) -> try unicode:characters_to_list(B) of L when is_list(L) -> L; diff --git a/lib/ssh/src/ssh_transport.erl b/lib/ssh/src/ssh_transport.erl index eaab13433a..a85926354e 100644 --- a/lib/ssh/src/ssh_transport.erl +++ b/lib/ssh/src/ssh_transport.erl @@ -61,14 +61,6 @@ -export([pack/3, adjust_algs_for_peer_version/2]). -export([decompress/2, decrypt_blocks/3, is_valid_mac/3 ]). % FIXME: remove --define(Estring(X), ?STRING((if is_binary(X) -> X; - is_list(X) -> list_to_binary(X); - X==undefined -> <<>> - end))). --define(Empint(X), (ssh_bits:mpint(X))/binary ). --define(Ebinary(X), ?STRING(X) ). --define(Euint32(X), ?UINT32(X) ). - %%%---------------------------------------------------------------------------- %%% %%% There is a difference between supported and default algorithms. The diff --git a/lib/ssh/src/ssh_userauth.hrl b/lib/ssh/src/ssh_userauth.hrl deleted file mode 100644 index 2cfc1f0f83..0000000000 --- a/lib/ssh/src/ssh_userauth.hrl +++ /dev/null @@ -1,78 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2005-2016. 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% -%% - -%% - -%%% Description: user authentication protocol - --define(SSH_MSG_USERAUTH_REQUEST, 50). --define(SSH_MSG_USERAUTH_FAILURE, 51). --define(SSH_MSG_USERAUTH_SUCCESS, 52). --define(SSH_MSG_USERAUTH_BANNER, 53). --define(SSH_MSG_USERAUTH_PK_OK, 60). --define(SSH_MSG_USERAUTH_PASSWD_CHANGEREQ, 60). --define(SSH_MSG_USERAUTH_INFO_REQUEST, 60). --define(SSH_MSG_USERAUTH_INFO_RESPONSE, 61). - --record(ssh_msg_userauth_request, - { - user, %% string - service, %% string - method, %% string "publickey", "password" - data %% opaque - }). - --record(ssh_msg_userauth_failure, - { - authentications, %% string - partial_success %% boolean - }). - --record(ssh_msg_userauth_success, - { - }). - --record(ssh_msg_userauth_banner, - { - message, %% string - language %% string - }). - --record(ssh_msg_userauth_passwd_changereq, - { - prompt, %% string - languge %% string - }). - --record(ssh_msg_userauth_pk_ok, - { - algorithm_name, % string - key_blob % string - }). - --record(ssh_msg_userauth_info_request, - {name, - instruction, - language_tag, - num_prompts, - data}). --record(ssh_msg_userauth_info_response, - {num_responses, - data}). diff --git a/lib/ssh/test/ssh_bench_SUITE.erl b/lib/ssh/test/ssh_bench_SUITE.erl index 880c519a5e..5ff7a71c45 100644 --- a/lib/ssh/test/ssh_bench_SUITE.erl +++ b/lib/ssh/test/ssh_bench_SUITE.erl @@ -26,7 +26,7 @@ -include_lib("ssh/src/ssh.hrl"). -include_lib("ssh/src/ssh_transport.hrl"). -include_lib("ssh/src/ssh_connect.hrl"). --include_lib("ssh/src/ssh_userauth.hrl"). +-include_lib("ssh/src/ssh_auth.hrl"). %%%================================================================ %%% diff --git a/lib/ssl/doc/src/notes.xml b/lib/ssl/doc/src/notes.xml index 155bee5ede..335896c60a 100644 --- a/lib/ssl/doc/src/notes.xml +++ b/lib/ssl/doc/src/notes.xml @@ -226,6 +226,87 @@ </section> +<section><title>SSL 9.2.3.5</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Handling of zero size fragments in TLS could cause an + infinite loop. This has now been corrected.</p> + <p> + Own Id: OTP-15328 Aux Id: ERIERL-379 </p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 9.2.3.4</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Hibernation now works as expected in all cases, was + accidently broken by optimization efforts.</p> + <p> + Own Id: OTP-15910</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 9.2.3.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Correct handshake handling, might cause strange symptoms + such as ASN.1 certificate decoding issues.</p> + <p> + Own Id: OTP-15879 Aux Id: ERL-968 </p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 9.2.3.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Returned "alert error string" is now same as logged alert + string</p> + <p> + Own Id: OTP-15844</p> + </item> + </list> + </section> + +</section> + +<section><title>SSL 9.2.3.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Correct solution for retaining tcp flow control OTP-15802 + (ERL-934) as to not break ssl:recv as reported in + (ERL-938)</p> + <p> + Own Id: OTP-15823 Aux Id: ERL-934, ERL-938 </p> + </item> + </list> + </section> + +</section> + <section><title>SSL 9.2.3</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index 3aa6e09c2c..05590666da 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -207,6 +207,10 @@ <datatype> <name name="sign_scheme"/> </datatype> + + <datatype> + <name name="group"/> + </datatype> <datatype> <name name="kex_algo"/> @@ -363,7 +367,20 @@ </p> </desc> </datatype> - + + <datatype> + <name name="supported_groups"/> + <desc> + <p>TLS 1.3 introduces the "supported_groups" extension that is used for negotiating + the Diffie-Hellman parameters in a TLS 1.3 handshake. Both client and server + can specify a list of parameters that they are willing to use. + </p> + <p> If it is not specified it will use a default list ([x25519, x448, secp256r1, secp384r1]) that + is filtered based on the installed crypto library version. + </p> + </desc> + </datatype> + <datatype> <name name="secure_renegotiation"/> <desc><p>Specifies if to reject renegotiation attempt that does @@ -919,6 +936,8 @@ fun(srp, Username :: string(), UserState :: term()) -> <name name="dh_der"/> <desc><p>The DER-encoded Diffie-Hellman parameters. If specified, it overrides option <c>dhfile</c>.</p> + <warning><p>The <c>dh_der</c> option is not supported by TLS 1.3. Use the + <c>supported_groups</c> option instead.</p></warning> </desc> </datatype> @@ -928,6 +947,8 @@ fun(srp, Username :: string(), UserState :: term()) -> parameters to be used by the server if a cipher suite using Diffie Hellman key exchange is negotiated. If not specified, default parameters are used.</p> + <warning><p>The <c>dh_file</c> option is not supported by TLS 1.3. Use the + <c>supported_groups</c> option instead.</p></warning> </desc> </datatype> diff --git a/lib/ssl/doc/src/standards_compliance.xml b/lib/ssl/doc/src/standards_compliance.xml index ca98385f85..9df48b99d3 100644 --- a/lib/ssl/doc/src/standards_compliance.xml +++ b/lib/ssl/doc/src/standards_compliance.xml @@ -126,29 +126,28 @@ <section> <title>TLS 1.3</title> - <p>OTP-22 introduces basic support for TLS 1.3 on the server side. Basic functionality + <p>OTP-22 introduces basic support for TLS 1.3. Basic functionality covers a simple TLS 1.3 handshake with support of the mandatory extensions (supported_groups, signature_algorithms, key_share, supported_versions and - signature_algorithms_cert). The server supports a selective set of cryptographic algorithms:</p> + signature_algorithms_cert). The current implementation supports a selective set of cryptographic algorithms:</p> <list type="bulleted"> <item>Key Exchange: ECDHE</item> <item>Groups: all standard groups supported for the Diffie-Hellman key exchange</item> <item>Ciphers: TLS_AES_128_GCM_SHA256, TLS_AES_256_GCM_SHA384, TLS_CHACHA20_POLY1305_SHA256 and TLS_AES_128_CCM_SHA256</item> - <item>Signature Algorithms: RSA and RSA PSS</item> - <item>Certificates: currently only certificates with RSA keys are supported</item> + <item>Signature Algorithms: rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pkcs1_sha512, + ecdsa_secp256r1_sha256, ecdsa_secp384r1_sha384, ecdsa_secp521r1_sha512, rsa_pss_rsae_sha256, + rsa_pss_rsae_sha384, rsa_pss_rsae_sha512, rsa_pkcs1_sha1 and ecdsa_sha1</item> + <item>Certificates: RSA (it MUST use the rsaEncryption OID) and ECDSA keys</item> </list> <p>Other notable features:</p> <list type="bulleted"> - <item>The server supports the HelloRetryRequest mechanism</item> <item>PSK and session resumption not supported</item> <item>Early data and 0-RTT not supported</item> <item>Key and Initialization Vector Update not supported</item> </list> <p>For more detailed information see the <seealso marker="#soc_table">Standards Compliance</seealso> below.</p> - <warning><p>Note that the client side is not yet functional. It is planned to be released - later in OTP-22.</p></warning> <p> The following table describes the current state of standards compliance for TLS 1.3.</p> <p>(<em>C</em> = Compliant, <em>NC</em> = Non-Compliant, <em>PC</em> = Partially-Compliant, @@ -176,25 +175,25 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Version downgrade protection mechanism</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">RSASSA-PSS signature schemes</cell> <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_versions (ClientHello) extension</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms_cert extension</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -211,7 +210,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">(EC)DHE</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -295,8 +294,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -319,14 +318,14 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_groups (RFC7919)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -343,8 +342,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">application_layer_protocol_negotiation (RFC7301)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -373,8 +372,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">key_share (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -403,8 +402,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_versions (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -427,8 +426,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms_cert (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -459,13 +458,13 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_groups (RFC7919)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -482,8 +481,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">application_layer_protocol_negotiation (RFC7301)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -513,7 +512,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">key_share (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -543,7 +542,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_versions (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -567,7 +566,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms_cert (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -577,20 +576,20 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Version downgrade protection</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">key_share (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -601,8 +600,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_versions (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -615,13 +614,13 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Version downgrade protection</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">key_share (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -633,7 +632,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_versions (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -650,7 +649,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">key_share (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -662,7 +661,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">supported_versions (RFC8446)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -672,8 +671,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -706,62 +705,62 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha256</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha384</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha512</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_secp256r1_sha256</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_secp384r1_sha384</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_secp521r1_sha512</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pss_rsae_sha256</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pss_rsae_sha384</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pss_rsae_sha512</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -796,14 +795,14 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha1</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_sha1</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -816,55 +815,55 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha384</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha512</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_secp256r1_sha256</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_secp384r1_sha384</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_secp521r1_sha512</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pss_rsae_sha256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pss_rsae_sha384</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pss_rsae_sha512</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -900,13 +899,13 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">rsa_pkcs1_sha1</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ecdsa_sha1</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -967,68 +966,68 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">secp256r1</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">secp384r1</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">secp521r1</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">x25519</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">x448</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ffdhe2048</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ffdhe3072</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ffdhe4096</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ffdhe6144</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">ffdhe8192</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -1105,8 +1104,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1224,8 +1223,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1362,8 +1361,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1374,8 +1373,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1398,8 +1397,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms_cert (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -1417,8 +1416,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1441,8 +1440,8 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">signature_algorithms_cert (RFC8446)</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -1463,8 +1462,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1521,73 +1520,82 @@ 4.4.2.2. Server Certificate Selection </url> </cell> - <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">certificate type MUST be X.509v3</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle">The certificate type MUST be X.509v3, unless explicitly + negotiated otherwise</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">certificate's public key is compatible</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle">The server's end-entity certificate's public key (and associated + restrictions) MUST be compatible with the selected authentication + algorithm from the client's "signature_algorithms" extension + (currently RSA, ECDSA, or EdDSA).</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">The certificate MUST allow the key to be used for signing</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle">The certificate MUST allow the key to be used for signing + with a signature scheme indicated in the client's "signature_algorithms"/"signature_algorithms_cert" + extensions</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">server_name and certificate_authorities are used</cell> + <cell align="left" valign="middle">The "server_name" and "certificate_authorities" + extensions are used to guide certificate selection. As servers + MAY require the presence of the "server_name" extension, clients + SHOULD send this extension, when applicable.</cell> <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"></cell> </row> <row> - <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle"><em>Server</em></cell> + <cell align="left" valign="middle"> + <url href="https://tools.ietf.org/html/rfc8446#section-4.4.2.3"> + 4.4.2.3. Client Certificate Selection + </url> + </cell> + <cell align="left" valign="middle"><em></em></cell> <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">certificate type MUST be X.509v3</cell> + <cell align="left" valign="middle">The certificate type MUST be X.509v3, unless explicitly + negotiated otherwise</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle"><em>22</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">certificate's public key is compatible</cell> - <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle"><em>22</em></cell> + <cell align="left" valign="middle">If the "certificate_authorities" extension in the + CertificateRequest message was present, at least one of the + certificates in the certificate chain SHOULD be issued by one of + the listed CAs.</cell> + <cell align="left" valign="middle"><em>NC</em></cell> + <cell align="left" valign="middle"><em></em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">The certificate MUST allow the key to be used for signing</cell> + <cell align="left" valign="middle">The certificates MUST be signed using an acceptable signature + algorithm</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle"><em>22</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">server_name and certificate_authorities are used</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> - </row> - - <row> - <cell align="left" valign="middle"> - <url href="https://tools.ietf.org/html/rfc8446#section-4.4.2.3"> - 4.4.2.3. Client Certificate Selection - </url> - </cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle">If the CertificateRequest message contained a non-empty + "oid_filters" extension, the end-entity certificate MUST match the + extension OIDs that are recognized by the client</cell> <cell align="left" valign="middle"><em>NC</em></cell> <cell align="left" valign="middle"><em></em></cell> </row> @@ -1599,8 +1607,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1616,8 +1624,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1633,8 +1641,8 @@ </url> </cell> <cell align="left" valign="middle"><em>Client</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -1738,25 +1746,25 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST NOT be interleaved with other record types</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST NOT span key changes</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST NOT send zero-length fragments</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Alert messages MUST NOT be fragmented</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -1807,7 +1815,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">The padding sent is automatically verified</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -1950,51 +1958,51 @@ </url> </cell> <cell align="left" valign="middle"><em></em></cell> - <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle"><em>22</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST implement the TLS_AES_128_GCM_SHA256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">SHOULD implement the TLS_AES_256_GCM_SHA384</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">SHOULD implement the TLS_CHACHA20_POLY1305_SHA256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>Digital signatures</em></cell> - <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle"><em>22</em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST support rsa_pkcs1_sha256 (for certificates)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST support rsa_pss_rsae_sha256 (for CertificateVerify and certificates)</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST support ecdsa_secp256r1_sha256</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -2007,13 +2015,13 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">MUST support key exchange with secp256r1</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">SHOULD support key exchange with X25519</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> @@ -2030,7 +2038,7 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Supported Versions</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -2042,25 +2050,25 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Signature Algorithms</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Signature Algorithms Certificate</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Negotiated Groups</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">Key Share</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -2072,32 +2080,32 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>MUST send and use these extensions</em></cell> - <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>PC</em></cell> <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">"supported_versions" is REQUIRED for ClientHello, ServerHello and HelloRetryRequest</cell> - <cell align="left" valign="middle"><em>PC</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">"signature_algorithms" is REQUIRED for certificate authentication</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">"supported_groups" is REQUIRED for ClientHello messages using (EC)DHE key exchange</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">"key_share" is REQUIRED for (EC)DHE key exchange</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> @@ -2115,20 +2123,20 @@ <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>TLS 1.3 ClientHello</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">If not containing a "pre_shared_key" extension, it MUST contain both a "signature_algorithms" extension and a "supported_groups" extension.</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">If containing a "supported_groups" extension, it MUST also contain a "key_share" extension, and vice versa. An empty KeyShare.client_shares vector is permitted.</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> @@ -2151,30 +2159,44 @@ </url> </cell> <cell align="left" valign="middle"><em></em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle"><em>MUST correctly handle extensible fields</em></cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"><em></em></cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">A client sending a ClientHello MUST support all parameters advertised in it.</cell> - <cell align="left" valign="middle"><em>NC</em></cell> - <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">A client sending a ClientHello MUST support all parameters + advertised in it. Otherwise, the server may fail to interoperate by selecting one of those parameters.</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">A middlebox which terminates a TLS connection MUST behave as a compliant TLS server</cell> + <cell align="left" valign="middle">A server receiving a ClientHello MUST correctly ignore all + unrecognized cipher suites, extensions, and other parameters. Otherwise, it may fail to + interoperate with newer clients. In TLS 1.3, a client receiving a CertificateRequest or + NewSessionTicket MUST also ignore all unrecognized extensions.</cell> + <cell align="left" valign="middle"><em>C</em></cell> + <cell align="left" valign="middle"><em>22.1</em></cell> + </row> + + <row> + <cell align="left" valign="middle"></cell> + <cell align="left" valign="middle">A middlebox which terminates a TLS connection MUST behave as a + compliant TLS server</cell> <cell align="left" valign="middle"><em>NA</em></cell> <cell align="left" valign="middle"></cell> </row> <row> <cell align="left" valign="middle"></cell> - <cell align="left" valign="middle">A middlebox which forwards ClientHello parameters it does not understand MUST NOT process any messages beyond that ClientHello.</cell> + <cell align="left" valign="middle">A middlebox which forwards ClientHello parameters it does not + understand MUST NOT process any messages beyond that ClientHello. It MUST forward all subsequent + traffic unmodified. Otherwise, it may fail to interoperate with newer clients and servers.</cell> <cell align="left" valign="middle"><em>NA</em></cell> <cell align="left" valign="middle"></cell> </row> @@ -2193,25 +2215,25 @@ <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">TLS_AES_128_GCM_SHA256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">TLS_AES_256_GCM_SHA384</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">TLS_CHACHA20_POLY1305_SHA256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> <cell align="left" valign="middle">TLS_AES_128_CCM_SHA256</cell> <cell align="left" valign="middle"><em>C</em></cell> - <cell align="left" valign="middle">22</cell> + <cell align="left" valign="middle"><em>22</em></cell> </row> <row> <cell align="left" valign="middle"></cell> diff --git a/lib/ssl/src/dtls_connection.erl b/lib/ssl/src/dtls_connection.erl index 6928d7a93d..b220691e79 100644 --- a/lib/ssl/src/dtls_connection.erl +++ b/lib/ssl/src/dtls_connection.erl @@ -836,9 +836,12 @@ initial_flight_state(_) -> next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{ dtls_record_buffer = Buf0, dtls_cipher_texts = CT0} = Buffers, + connection_env = #connection_env{negotiated_version = Version}, + static_env = #static_env{data_tag = DataTag}, ssl_options = SslOpts} = State0) -> case dtls_record:get_dtls_records(Data, - acceptable_record_versions(StateName, State0), + {DataTag, StateName, Version, + [dtls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_DATAGRAM_VERSIONS]}, Buf0, SslOpts) of {Records, Buf1} -> CT1 = CT0 ++ Records, @@ -849,10 +852,6 @@ next_dtls_record(Data, StateName, #state{protocol_buffers = #protocol_buffers{ Alert end. -acceptable_record_versions(hello, _) -> - [dtls_record:protocol_version(Vsn) || Vsn <- ?ALL_AVAILABLE_DATAGRAM_VERSIONS]; -acceptable_record_versions(_, #state{connection_env = #connection_env{negotiated_version = Version}}) -> - [Version]. dtls_handshake_events(Packets) -> lists:map(fun(Packet) -> diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index d8c0e30973..4a381745d4 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -255,7 +255,8 @@ enc_handshake(#client_hello{client_version = {Major, Minor}, CmLength = byte_size(BinCompMethods), BinCipherSuites = list_to_binary(CipherSuites), CsLength = byte_size(BinCipherSuites), - ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions), + ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions, + dtls_v1:corresponding_tls_version({Major, Minor})), {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SIDLength), SessionID/binary, diff --git a/lib/ssl/src/dtls_record.erl b/lib/ssl/src/dtls_record.erl index a4846f42c5..8b8db7b2de 100644 --- a/lib/ssl/src/dtls_record.erl +++ b/lib/ssl/src/dtls_record.erl @@ -162,26 +162,16 @@ current_connection_state_epoch(#{current_write := #{epoch := Epoch}}, Epoch. %%-------------------------------------------------------------------- --spec get_dtls_records(binary(), [ssl_record:ssl_version()], binary(), +-spec get_dtls_records(binary(), {atom(), atom(), ssl_record:ssl_version(), [ssl_record:ssl_version()]}, binary(), #ssl_options{}) -> {[binary()], binary()} | #alert{}. %% %% Description: Given old buffer and new data from UDP/SCTP, packs up a records %% and returns it as a list of tls_compressed binaries also returns leftover %% data %%-------------------------------------------------------------------- -get_dtls_records(Data, Versions, Buffer, SslOpts) -> +get_dtls_records(Data, Vinfo, Buffer, SslOpts) -> BinData = list_to_binary([Buffer, Data]), - case erlang:byte_size(BinData) of - N when N >= 3 -> - case assert_version(BinData, Versions) of - true -> - get_dtls_records_aux(BinData, [], SslOpts); - false -> - ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) - end; - _ -> - get_dtls_records_aux(BinData, [], SslOpts) - end. + get_dtls_records_aux(Vinfo, BinData, [], SslOpts). %%==================================================================== %% Encoding DTLS records @@ -405,52 +395,49 @@ initial_connection_state(ConnectionEnd, BeastMitigation) -> client_verify_data => undefined, server_verify_data => undefined }. -assert_version(<<?BYTE(_), ?BYTE(MajVer), ?BYTE(MinVer), _/binary>>, Versions) -> - is_acceptable_version({MajVer, MinVer}, Versions). -get_dtls_records_aux(<<?BYTE(?APPLICATION_DATA),?BYTE(MajVer),?BYTE(MinVer), - ?UINT16(Epoch), ?UINT48(SequenceNumber), - ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, - Acc, SslOpts) -> - ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), - get_dtls_records_aux(Rest, [#ssl_tls{type = ?APPLICATION_DATA, - version = {MajVer, MinVer}, - epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc], SslOpts); -get_dtls_records_aux(<<?BYTE(?HANDSHAKE),?BYTE(MajVer),?BYTE(MinVer), - ?UINT16(Epoch), ?UINT48(SequenceNumber), - ?UINT16(Length), - Data:Length/binary, Rest/binary>> = RawDTLSRecord, - Acc, SslOpts) when MajVer >= 128 -> - ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), - get_dtls_records_aux(Rest, [#ssl_tls{type = ?HANDSHAKE, - version = {MajVer, MinVer}, - epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc], SslOpts); -get_dtls_records_aux(<<?BYTE(?ALERT),?BYTE(MajVer),?BYTE(MinVer), - ?UINT16(Epoch), ?UINT48(SequenceNumber), - ?UINT16(Length), Data:Length/binary, - Rest/binary>> = RawDTLSRecord, Acc, SslOpts) -> +get_dtls_records_aux({DataTag, StateName, _, Versions} = Vinfo, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer), + ?UINT16(Epoch), ?UINT48(SequenceNumber), + ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, + Acc, SslOpts) when ((StateName == hello) orelse + ((StateName == certify) andalso (DataTag == udp)) orelse + ((StateName == abbreviated) andalso(DataTag == udp))) + andalso + ((Type == ?HANDSHAKE) orelse + (Type == ?ALERT)) -> ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), - get_dtls_records_aux(Rest, [#ssl_tls{type = ?ALERT, - version = {MajVer, MinVer}, - epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc], SslOpts); -get_dtls_records_aux(<<?BYTE(?CHANGE_CIPHER_SPEC),?BYTE(MajVer),?BYTE(MinVer), + case is_acceptable_version({MajVer, MinVer}, Versions) of + true -> + get_dtls_records_aux(Vinfo, Rest, [#ssl_tls{type = Type, + version = {MajVer, MinVer}, + epoch = Epoch, sequence_number = SequenceNumber, + fragment = Data} | Acc], SslOpts); + false -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end; +get_dtls_records_aux({_, _, Version, _} = Vinfo, <<?BYTE(Type),?BYTE(MajVer),?BYTE(MinVer), ?UINT16(Epoch), ?UINT48(SequenceNumber), ?UINT16(Length), Data:Length/binary, Rest/binary>> = RawDTLSRecord, - Acc, SslOpts) -> + Acc, SslOpts) when (Type == ?APPLICATION_DATA) orelse + (Type == ?HANDSHAKE) orelse + (Type == ?ALERT) orelse + (Type == ?CHANGE_CIPHER_SPEC) -> ssl_logger:debug(SslOpts#ssl_options.log_level, inbound, 'record', [RawDTLSRecord]), - get_dtls_records_aux(Rest, [#ssl_tls{type = ?CHANGE_CIPHER_SPEC, - version = {MajVer, MinVer}, - epoch = Epoch, sequence_number = SequenceNumber, - fragment = Data} | Acc], SslOpts); -get_dtls_records_aux(<<?BYTE(_), ?BYTE(_MajVer), ?BYTE(_MinVer), + case {MajVer, MinVer} of + Version -> + get_dtls_records_aux(Vinfo, Rest, [#ssl_tls{type = Type, + version = {MajVer, MinVer}, + epoch = Epoch, sequence_number = SequenceNumber, + fragment = Data} | Acc], SslOpts); + _ -> + ?ALERT_REC(?FATAL, ?BAD_RECORD_MAC) + end; +get_dtls_records_aux(_, <<?BYTE(_), ?BYTE(_MajVer), ?BYTE(_MinVer), ?UINT16(Length), _/binary>>, _Acc, _) when Length > ?MAX_CIPHER_TEXT_LENGTH -> ?ALERT_REC(?FATAL, ?RECORD_OVERFLOW); -get_dtls_records_aux(Data, Acc, _) -> +get_dtls_records_aux(_, Data, Acc, _) -> case size(Data) =< ?MAX_CIPHER_TEXT_LENGTH + ?INITIAL_BYTES of true -> {lists:reverse(Acc), Data}; diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 6af65e09f2..7ff9aed8ea 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -125,7 +125,11 @@ protocol_extensions/0, session_id/0, error_alert/0, - srp_param_type/0]). + tls_alert/0, + srp_param_type/0, + named_curve/0, + sign_scheme/0, + group/0]). %% ------------------------------------------------------------------------------------------------------- @@ -191,7 +195,8 @@ | rsa_pss_pss_sha384 | rsa_pss_pss_sha512 | rsa_pkcs1_sha1 - | ecdsa_sha1. + | ecdsa_sha1. % exported + -type kex_algo() :: rsa | dhe_rsa | dhe_dss | ecdhe_ecdsa | ecdh_ecdsa | ecdh_rsa | @@ -236,10 +241,10 @@ sect163r2 | secp160k1 | secp160r1 | - secp160r2. + secp160r2. % exported -type group() :: secp256r1 | secp384r1 | secp521r1 | ffdhe2048 | - ffdhe3072 | ffdhe4096 | ffdhe6144 | ffdhe8192. + ffdhe3072 | ffdhe4096 | ffdhe6144 | ffdhe8192. % exported -type srp_param_type() :: srp_1024 | srp_1536 | @@ -279,7 +284,7 @@ bad_certificate_status_response | bad_certificate_hash_value | unknown_psk_identity | - no_application_protocol. + no_application_protocol. % exported %% ------------------------------------------------------------------------------------------------------- -type common_option() :: {protocol, protocol()} | @@ -292,6 +297,7 @@ {ciphers, cipher_suites()} | {eccs, [named_curve()]} | {signature_algs_cert, signature_schemes()} | + {supported_groups, supported_groups()} | {secure_renegotiate, secure_renegotiation()} | {depth, allowed_cert_chain_length()} | {verify_fun, custom_verify()} | @@ -338,6 +344,7 @@ -type protocol_versions() :: [protocol_version()]. -type signature_algs() :: [{hash(), sign_algo()}]. -type signature_schemes() :: [sign_scheme()]. +-type supported_groups() :: [group()]. -type custom_user_lookup() :: {Lookupfun :: fun(), UserState :: any()}. -type padding_check() :: boolean(). -type beast_mitigation() :: one_n_minus_one | zero_n | disabled. @@ -975,7 +982,8 @@ cipher_suites(all) -> %% Description: Returns all default and all supported cipher suites for a %% TLS/DTLS version %%-------------------------------------------------------------------- -cipher_suites(Base, Version) when Version == 'tlsv1.2'; +cipher_suites(Base, Version) when Version == 'tlsv1.3'; + Version == 'tlsv1.2'; Version == 'tlsv1.1'; Version == tlsv1; Version == sslv3 -> @@ -1909,7 +1917,7 @@ validate_option(Opt, Value) -> throw({error, {options, {Opt, Value}}}). handle_cb_info({V1, V2, V3, V4}, {_,_,_,_,_}) -> - {V1,V2,V3,V4, list_to_atom(atom_to_list(V2) ++ "passive")}; + {V1,V2,V3,V4, list_to_atom(atom_to_list(V2) ++ "_passive")}; handle_cb_info(CbInfo, _) -> CbInfo. diff --git a/lib/ssl/src/ssl_cipher.erl b/lib/ssl/src/ssl_cipher.erl index 4da50d2af8..c16e2331ff 100644 --- a/lib/ssl/src/ssl_cipher.erl +++ b/lib/ssl/src/ssl_cipher.erl @@ -38,7 +38,7 @@ cipher_init/3, nonce_seed/2, decipher/6, cipher/5, aead_encrypt/6, aead_decrypt/6, suites/1, all_suites/1, crypto_support_filters/0, chacha_suites/1, anonymous_suites/1, psk_suites/1, psk_suites_anon/1, - srp_suites/0, srp_suites_anon/0, + srp_suites/1, srp_suites_anon/1, rc4_suites/1, des_suites/1, rsa_suites/1, filter/3, filter_suites/1, filter_suites/2, hash_algorithm/1, sign_algorithm/1, is_acceptable_hash/2, is_fallback/1, @@ -284,7 +284,7 @@ all_suites({3, _} = Version) -> suites(Version) ++ chacha_suites(Version) ++ psk_suites(Version) - ++ srp_suites() + ++ srp_suites(Version) ++ rc4_suites(Version) ++ des_suites(Version) ++ rsa_suites(Version); @@ -313,8 +313,8 @@ chacha_suites(_) -> %% Description: Returns a list of the anonymous cipher suites, only supported %% if explicitly set by user. Intended only for testing. %%-------------------------------------------------------------------- -anonymous_suites({3, N}) -> - srp_suites_anon() ++ anonymous_suites(N); +anonymous_suites({3, N} = Version) -> + srp_suites_anon(Version) ++ anonymous_suites(N); anonymous_suites({254, _} = Version) -> dtls_v1:anonymous_suites(Version); anonymous_suites(4) -> @@ -375,7 +375,7 @@ psk_suites(_) -> %%-------------------------------------------------------------------- psk_suites_anon({3, N}) -> psk_suites_anon(N); -psk_suites_anon(3) -> +psk_suites_anon(3 = N) -> [ ?TLS_DHE_PSK_WITH_AES_256_GCM_SHA384, ?TLS_PSK_WITH_AES_256_GCM_SHA384, @@ -401,8 +401,8 @@ psk_suites_anon(3) -> ?TLS_PSK_WITH_AES_128_CCM, ?TLS_PSK_WITH_AES_128_CCM_8, ?TLS_ECDHE_PSK_WITH_RC4_128_SHA - ] ++ psk_suites_anon(0); -psk_suites_anon(_) -> + ] ++ psk_suites_anon(N-1); +psk_suites_anon(N) when N > 0 -> [?TLS_DHE_PSK_WITH_AES_256_CBC_SHA, ?TLS_PSK_WITH_AES_256_CBC_SHA, ?TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA, @@ -413,14 +413,18 @@ psk_suites_anon(_) -> ?TLS_PSK_WITH_3DES_EDE_CBC_SHA, ?TLS_ECDHE_PSK_WITH_RC4_128_SHA, ?TLS_DHE_PSK_WITH_RC4_128_SHA, - ?TLS_PSK_WITH_RC4_128_SHA]. + ?TLS_PSK_WITH_RC4_128_SHA]; +psk_suites_anon(0) -> + []. %%-------------------------------------------------------------------- --spec srp_suites() -> [ssl_cipher_format:cipher_suite()]. +-spec srp_suites(tls_record:tls_version()) -> [ssl_cipher_format:cipher_suite()]. %% %% Description: Returns a list of the SRP cipher suites, only supported %% if explicitly set by user. %%-------------------------------------------------------------------- -srp_suites() -> +srp_suites({3,0}) -> + []; +srp_suites(_) -> [?TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA, ?TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA, ?TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA, @@ -429,12 +433,14 @@ srp_suites() -> ?TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA]. %%-------------------------------------------------------------------- --spec srp_suites_anon() -> [ssl_cipher_format:cipher_suite()]. +-spec srp_suites_anon(tls_record:tls_version()) -> [ssl_cipher_format:cipher_suite()]. %% %% Description: Returns a list of the SRP anonymous cipher suites, only supported %% if explicitly set by user. %%-------------------------------------------------------------------- -srp_suites_anon() -> +srp_suites_anon({3,0}) -> + []; +srp_suites_anon(_) -> [?TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA, ?TLS_SRP_SHA_WITH_AES_128_CBC_SHA, ?TLS_SRP_SHA_WITH_AES_256_CBC_SHA]. @@ -973,15 +979,25 @@ scheme_to_components(ecdsa_sha1) -> {sha1, ecdsa, undefined}; scheme_to_components({Hash,Sign}) -> {Hash, Sign, undefined}. -%% TODO: Add support for EC and RSA-SSA signatures -signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha1WithRSAEncryption}) -> - rsa_pkcs1_sha1; +%% TODO: Add support for ed25519, ed448, rsa_pss* signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha256WithRSAEncryption}) -> rsa_pkcs1_sha256; signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha384WithRSAEncryption}) -> rsa_pkcs1_sha384; signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha512WithRSAEncryption}) -> - rsa_pkcs1_sha512. + rsa_pkcs1_sha512; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA256'}) -> + ecdsa_secp256r1_sha256; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA384'}) -> + ecdsa_secp384r1_sha384; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA512'}) -> + ecdsa_secp512r1_sha512; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'sha-1WithRSAEncryption'}) -> + rsa_pkcs1_sha1; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?sha1WithRSAEncryption}) -> + rsa_pkcs1_sha1; +signature_algorithm_to_scheme(#'SignatureAlgorithm'{algorithm = ?'ecdsa-with-SHA1'}) -> + ecdsa_sha1. %% RFC 5246: 6.2.3.2. CBC Block Cipher diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index a8cb9ea815..2483509228 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -614,7 +614,8 @@ read_application_dist_data(DHandle, Front0, BufferSize, Rear0, Bin0) -> <<SizeA:32, DataA:SizeA/binary, SizeB:32, DataB:SizeB/binary, SizeC:32, DataC:SizeC/binary, - SizeD:32, DataD:SizeD/binary, Rest/binary>> -> + SizeD:32, DataD:SizeD/binary, Rest/binary>> + when 0 < SizeA, 0 < SizeB, 0 < SizeC, 0 < SizeD -> %% We have 4 complete packets in the first binary erlang:dist_ctrl_put_data(DHandle, DataA), erlang:dist_ctrl_put_data(DHandle, DataB), @@ -624,7 +625,8 @@ read_application_dist_data(DHandle, Front0, BufferSize, Rear0, Bin0) -> DHandle, Front0, BufferSize - (4*4+SizeA+SizeB+SizeC+SizeD), Rear0, Rest); <<SizeA:32, DataA:SizeA/binary, SizeB:32, DataB:SizeB/binary, - SizeC:32, DataC:SizeC/binary, Rest/binary>> -> + SizeC:32, DataC:SizeC/binary, Rest/binary>> + when 0 < SizeA, 0 < SizeB, 0 < SizeC -> %% We have 3 complete packets in the first binary erlang:dist_ctrl_put_data(DHandle, DataA), erlang:dist_ctrl_put_data(DHandle, DataB), @@ -632,7 +634,8 @@ read_application_dist_data(DHandle, Front0, BufferSize, Rear0, Bin0) -> read_application_dist_data( DHandle, Front0, BufferSize - (3*4+SizeA+SizeB+SizeC), Rear0, Rest); <<SizeA:32, DataA:SizeA/binary, - SizeB:32, DataB:SizeB/binary, Rest/binary>> -> + SizeB:32, DataB:SizeB/binary, Rest/binary>> + when 0 < SizeA, 0 < SizeB -> %% We have 2 complete packets in the first binary erlang:dist_ctrl_put_data(DHandle, DataA), erlang:dist_ctrl_put_data(DHandle, DataB), @@ -643,13 +646,13 @@ read_application_dist_data(DHandle, Front0, BufferSize, Rear0, Bin0) -> %% Basic one packet code path <<Size:32, Data:Size/binary, Rest/binary>> -> %% We have a complete packet in the first binary - erlang:dist_ctrl_put_data(DHandle, Data), + 0 < Size andalso erlang:dist_ctrl_put_data(DHandle, Data), read_application_dist_data(DHandle, Front0, BufferSize - (4+Size), Rear0, Rest); <<Size:32, FirstData/binary>> when 4+Size =< BufferSize -> %% We have a complete packet in the buffer %% - fetch the missing content from the buffer front {Data,Front,Rear} = iovec_from_front(Size - byte_size(FirstData), Front0, Rear0, [FirstData]), - erlang:dist_ctrl_put_data(DHandle, Data), + 0 < Size andalso erlang:dist_ctrl_put_data(DHandle, Data), read_application_dist_data(DHandle, Front, BufferSize - (4+Size), Rear); <<Bin/binary>> -> %% In OTP-21 the match context reuse optimization fails if we use Bin0 in recursion, so here we @@ -665,23 +668,61 @@ read_application_dist_data(DHandle, Front0, BufferSize, Rear0, Bin0) -> %% contains enough data to maybe form a packet %% - fetch a tiny binary from the buffer front to complete the length field {LengthField,Front,Rear} = - iovec_from_front(4 - byte_size(IncompleteLengthField), Front0, Rear0, [IncompleteLengthField]), + case IncompleteLengthField of + <<>> -> + iovec_from_front(4, Front0, Rear0, []); + _ -> + iovec_from_front( + 4 - byte_size(IncompleteLengthField), Front0, Rear0, [IncompleteLengthField]) + end, LengthBin = iolist_to_binary(LengthField), read_application_dist_data(DHandle, Front, BufferSize, Rear, LengthBin); <<IncompleteLengthField/binary>> -> %% We do not have enough data in the buffer to even form a length field - await more data - {[IncompleteLengthField|Front0],BufferSize,Rear0} + case IncompleteLengthField of + <<>> -> + {Front0,BufferSize,Rear0}; + _ -> + {[IncompleteLengthField|Front0],BufferSize,Rear0} + end end end. +iovec_from_front(0, Front, Rear, Acc) -> + {lists:reverse(Acc),Front,Rear}; iovec_from_front(Size, [], Rear, Acc) -> - iovec_from_front(Size, lists:reverse(Rear), [], Acc); + case Rear of + %% Avoid lists:reverse/1 for simple cases. + %% Case clause for [] to avoid infinite loop. + [_] -> + iovec_from_front(Size, Rear, [], Acc); + [Bin2,Bin1] -> + iovec_from_front(Size, [Bin1,Bin2], [], Acc); + [Bin3,Bin2,Bin1] -> + iovec_from_front(Size, [Bin1,Bin2,Bin3], [], Acc); + [_,_,_|_] = Rear -> + iovec_from_front(Size, lists:reverse(Rear), [], Acc) + end; +iovec_from_front(Size, [Bin|Front], Rear, []) -> + case Bin of + <<Last:Size/binary>> -> % Just enough + {[Last],Front,Rear}; + <<Last:Size/binary, Rest/binary>> -> % More than enough, split here + {[Last],[Rest|Front],Rear}; + <<>> -> % Not enough, skip empty binaries + iovec_from_front(Size, Front, Rear, []); + <<_/binary>> -> % Not enough + BinSize = byte_size(Bin), + iovec_from_front(Size - BinSize, Front, Rear, [Bin]) + end; iovec_from_front(Size, [Bin|Front], Rear, Acc) -> case Bin of <<Last:Size/binary>> -> % Just enough {lists:reverse(Acc, [Last]),Front,Rear}; <<Last:Size/binary, Rest/binary>> -> % More than enough, split here {lists:reverse(Acc, [Last]),[Rest|Front],Rear}; + <<>> -> % Not enough, skip empty binaries + iovec_from_front(Size, Front, Rear, Acc); <<_/binary>> -> % Not enough BinSize = byte_size(Bin), iovec_from_front(Size - BinSize, Front, Rear, [Bin|Acc]) @@ -1195,7 +1236,7 @@ cipher(internal, #next_protocol{selected_protocol = SelectedProtocol}, #state{static_env = #static_env{role = server}, handshake_env = #handshake_env{expecting_finished = true, expecting_next_protocol_negotiation = true} = HsEnv} = State, Connection) -> - Connection:next_event(?FUNCTION_NAME, no_record, + Connection:next_event(?FUNCTION_NAME, no_record, State#state{handshake_env = HsEnv#handshake_env{negotiated_protocol = SelectedProtocol, expecting_next_protocol_negotiation = false}}); cipher(internal, #change_cipher_spec{type = <<1>>}, #state{handshake_env = HsEnv, connection_states = ConnectionStates0} = @@ -1234,10 +1275,17 @@ connection({call, From}, {connection_information, false}, State, _) -> Info = connection_info(State), hibernate_after(?FUNCTION_NAME, State, [{reply, From, {ok, Info}}]); connection({call, From}, negotiated_protocol, - #state{handshake_env = #handshake_env{negotiated_protocol = undefined}} = State, _) -> + #state{handshake_env = #handshake_env{alpn = undefined, + negotiated_protocol = undefined}} = State, _) -> hibernate_after(?FUNCTION_NAME, State, [{reply, From, {error, protocol_not_negotiated}}]); connection({call, From}, negotiated_protocol, - #state{handshake_env = #handshake_env{negotiated_protocol = SelectedProtocol}} = State, _) -> + #state{handshake_env = #handshake_env{alpn = undefined, + negotiated_protocol = SelectedProtocol}} = State, _) -> + hibernate_after(?FUNCTION_NAME, State, + [{reply, From, {ok, SelectedProtocol}}]); +connection({call, From}, negotiated_protocol, + #state{handshake_env = #handshake_env{alpn = SelectedProtocol, + negotiated_protocol = undefined}} = State, _) -> hibernate_after(?FUNCTION_NAME, State, [{reply, From, {ok, SelectedProtocol}}]); connection({call, From}, Msg, State, Connection) -> diff --git a/lib/ssl/src/ssl_connection.hrl b/lib/ssl/src/ssl_connection.hrl index ff7207a8ce..844368c761 100644 --- a/lib/ssl/src/ssl_connection.hrl +++ b/lib/ssl/src/ssl_connection.hrl @@ -66,6 +66,7 @@ sni_hostname = undefined, expecting_next_protocol_negotiation = false ::boolean(), next_protocol = undefined :: undefined | binary(), + alpn = undefined, %% Used in TLS 1.3 negotiated_protocol, hashsign_algorithm = {undefined, undefined}, cert_hashsign_algorithm = {undefined, undefined}, @@ -76,7 +77,7 @@ srp_params :: #srp_user{} | secret_printout() | 'undefined', public_key_info :: ssl_handshake:public_key_info() | 'undefined', premaster_secret :: binary() | secret_printout() | 'undefined', - server_psk_identity :: binary() | 'undefined' % server psk identity hint + server_psk_identity :: binary() | 'undefined' % server psk identity hint }). -record(connection_env, { diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index fbed7258c6..bd2efa9fbb 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -58,7 +58,7 @@ ]). %% Encode --export([encode_handshake/2, encode_hello_extensions/1, encode_extensions/1, encode_extensions/2, +-export([encode_handshake/2, encode_hello_extensions/2, encode_extensions/1, encode_extensions/2, encode_client_protocol_negotiation/2, encode_protocols_advertised_on_server/1]). %% Decode -export([decode_handshake/3, decode_vector/1, decode_hello_extensions/4, decode_extensions/3, @@ -76,7 +76,8 @@ handle_client_hello_extensions/9, %% Returns server hello extensions handle_server_hello_extensions/9, select_curve/2, select_curve/3, select_hashsign/4, select_hashsign/5, - select_hashsign_algs/3, empty_extensions/2, add_server_share/3 + select_hashsign_algs/3, empty_extensions/2, add_server_share/3, + add_alpn/2, add_selected_version/1, decode_alpn/1 ]). -export([get_cert_params/1, @@ -533,14 +534,14 @@ encode_handshake(#next_protocol{selected_protocol = SelectedProtocol}, _Version) PaddingLength = 32 - ((byte_size(SelectedProtocol) + 2) rem 32), {?NEXT_PROTOCOL, <<?BYTE((byte_size(SelectedProtocol))), SelectedProtocol/binary, ?BYTE(PaddingLength), 0:(PaddingLength * 8)>>}; -encode_handshake(#server_hello{server_version = {Major, Minor}, +encode_handshake(#server_hello{server_version = {Major, Minor} = Version, random = Random, session_id = Session_ID, cipher_suite = CipherSuite, compression_method = Comp_method, extensions = Extensions}, _Version) -> SID_length = byte_size(Session_ID), - ExtensionsBin = encode_hello_extensions(Extensions), + ExtensionsBin = encode_hello_extensions(Extensions, Version), {?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SID_length), Session_ID/binary, CipherSuite/binary, ?BYTE(Comp_method), ExtensionsBin/binary>>}; @@ -588,7 +589,9 @@ encode_handshake(#certificate_verify{signature = BinSig, hashsign_algorithm = Ha encode_handshake(#finished{verify_data = VerifyData}, _Version) -> {?FINISHED, VerifyData}. -encode_hello_extensions(Extensions) -> +encode_hello_extensions(_, {3, 0}) -> + <<>>; +encode_hello_extensions(Extensions, _) -> encode_extensions(hello_extensions_list(Extensions), <<>>). encode_extensions(Exts) -> @@ -706,7 +709,25 @@ encode_extensions([#key_share_server_hello{server_share = ServerShare0} | Rest], encode_extensions([#key_share_hello_retry_request{selected_group = Group0} | Rest], Acc) -> Group = tls_v1:group_to_enum(Group0), encode_extensions(Rest, <<?UINT16(?KEY_SHARE_EXT), - ?UINT16(2), ?UINT16(Group), Acc/binary>>). + ?UINT16(2), ?UINT16(Group), Acc/binary>>); +encode_extensions([#psk_key_exchange_modes{ke_modes = KEModes0} | Rest], Acc) -> + KEModes = encode_psk_key_exchange_modes(KEModes0), + KEModesLen = byte_size(KEModes), + ExtLen = KEModesLen + 1, + encode_extensions(Rest, <<?UINT16(?PSK_KEY_EXCHANGE_MODES_EXT), + ?UINT16(ExtLen), ?BYTE(KEModesLen), KEModes/binary, Acc/binary>>); +encode_extensions([#pre_shared_key_client_hello{ + offered_psks = #offered_psks{ + identities = Identities0, + binders = Binders0} = PSKs} | Rest], Acc) -> + Identities = encode_psk_identities(Identities0), + Binders = encode_psk_binders(Binders0), + Len = byte_size(Identities) + byte_size(Binders), + encode_extensions(Rest, <<?UINT16(?PRE_SHARED_KEY_EXT), + ?UINT16(Len), Identities/binary, Binders/binary, Acc/binary>>); +encode_extensions([#pre_shared_key_server_hello{selected_identity = Identity} | Rest], Acc) -> + encode_extensions(Rest, <<?UINT16(?PRE_SHARED_KEY_EXT), + ?UINT16(2), ?UINT16(Identity), Acc/binary>>). encode_client_protocol_negotiation(undefined, _) -> @@ -1165,6 +1186,13 @@ add_server_share(hello_retry_request, Extensions, Extensions#{key_share => #key_share_hello_retry_request{ selected_group = Group}}. +add_alpn(Extensions, ALPN0) -> + ALPN = encode_alpn([ALPN0], false), + Extensions#{alpn => ALPN}. + +add_selected_version(Extensions) -> + SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, + Extensions#{server_hello_selected_version => SupportedVersions}. kse_remove_private_key(#key_share_entry{ group = Group, @@ -1248,6 +1276,8 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, %% We also ignore the ALPN extension during renegotiation (see encode_alpn/2). [Protocol] when not Renegotiation -> {ConnectionStates, alpn, Protocol}; + [_] when Renegotiation -> + {ConnectionStates, alpn, undefined}; undefined -> NextProtocolNegotiation = maps:get(next_protocol_negotiation, Exts, undefined), Protocol = handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation), @@ -1478,8 +1508,12 @@ extension_value(#signature_algorithms_cert{signature_scheme_list = Schemes}) -> Schemes; extension_value(#key_share_client_hello{client_shares = ClientShares}) -> ClientShares; +extension_value(#key_share_server_hello{server_share = ServerShare}) -> + ServerShare; extension_value(#client_hello_versions{versions = Versions}) -> - Versions. + Versions; +extension_value(#server_hello_selected_version{selected_version = SelectedVersion}) -> + SelectedVersion. %%-------------------------------------------------------------------- @@ -2081,6 +2115,41 @@ encode_key_share_entry(#key_share_entry{ Len = byte_size(KeyExchange), <<?UINT16((tls_v1:group_to_enum(Group))),?UINT16(Len),KeyExchange/binary>>. +encode_psk_key_exchange_modes(KEModes) -> + encode_psk_key_exchange_modes(lists:reverse(KEModes), <<>>). +%% +encode_psk_key_exchange_modes([], Acc) -> + Acc; +encode_psk_key_exchange_modes([psk_ke|T], Acc) -> + encode_psk_key_exchange_modes(T, <<?BYTE(?PSK_KE),Acc/binary>>); +encode_psk_key_exchange_modes([psk_dhe_ke|T], Acc) -> + encode_psk_key_exchange_modes(T, <<?BYTE(?PSK_DHE_KE),Acc/binary>>). + + +encode_psk_identities(Identities) -> + encode_psk_identities(Identities, <<>>). +%% +encode_psk_identities([], Acc) -> + Len = byte_size(Acc), + <<?UINT16(Len), Acc/binary>>; +encode_psk_identities([#psk_identity{ + identity = Identity, + obfuscated_ticket_age = Age}|T], Acc) -> + IdLen = byte_size(Identity), + encode_psk_identities(T, <<Acc/binary,?UINT16(IdLen),Identity/binary,Age/binary>>). + + +encode_psk_binders(Binders) -> + encode_psk_binders(Binders, <<>>). +%% +encode_psk_binders([], Acc) -> + Len = byte_size(Acc), + <<?UINT16(Len), Acc/binary>>; +encode_psk_binders([Binder|T], Acc) -> + Len = byte_size(Binder), + encode_psk_binders(T, <<Acc/binary,?BYTE(Len),Binder/binary>>). + + hello_extensions_list(HelloExtensions) -> [Ext || {_, Ext} <- maps:to_list(HelloExtensions), Ext =/= undefined]. @@ -2433,6 +2502,33 @@ decode_extensions(<<?UINT16(?KEY_SHARE_EXT), ?UINT16(Len), #key_share_hello_retry_request{ selected_group = tls_v1:enum_to_group(Group)}}); +decode_extensions(<<?UINT16(?PSK_KEY_EXCHANGE_MODES_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, Version, MessageType, Acc) -> + <<?BYTE(PLen),KEModes:PLen/binary>> = ExtData, + decode_extensions(Rest, Version, MessageType, + Acc#{psk_key_exchange_modes => + #psk_key_exchange_modes{ + ke_modes = decode_psk_key_exchange_modes(KEModes)}}); + +decode_extensions(<<?UINT16(?PRE_SHARED_KEY_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, + Version, MessageType = client_hello, Acc) -> + <<?UINT16(IdLen),Identities:IdLen/binary,?UINT16(BLen),Binders:BLen/binary>> = ExtData, + decode_extensions(Rest, Version, MessageType, + Acc#{pre_shared_key => + #pre_shared_key_client_hello{ + offered_psks = #offered_psks{ + identities = decode_psk_identities(Identities), + binders = decode_psk_binders(Binders)}}}); + +decode_extensions(<<?UINT16(?PRE_SHARED_KEY_EXT), ?UINT16(Len), + ExtData:Len/binary, Rest/binary>>, + Version, MessageType = server_hello, Acc) -> + <<?UINT16(Identity)>> = ExtData, + decode_extensions(Rest, Version, MessageType, + Acc#{pre_shared_key => + #pre_shared_key_server_hello{ + selected_identity = Identity}}); %% Ignore data following the ClientHello (i.e., %% extensions) if not understood. @@ -2492,6 +2588,38 @@ decode_protocols(<<?BYTE(Len), Protocol:Len/binary, Rest/binary>>, Acc) -> decode_protocols(_Bytes, _Acc) -> {error, invalid_protocols}. + +decode_psk_key_exchange_modes(KEModes) -> + decode_psk_key_exchange_modes(KEModes, []). +%% +decode_psk_key_exchange_modes(<<>>, Acc) -> + lists:reverse(Acc); +decode_psk_key_exchange_modes(<<?BYTE(?PSK_KE), Rest/binary>>, Acc) -> + decode_psk_key_exchange_modes(Rest, [psk_ke|Acc]); +decode_psk_key_exchange_modes(<<?BYTE(?PSK_DHE_KE), Rest/binary>>, Acc) -> + decode_psk_key_exchange_modes(Rest, [psk_dhe_ke|Acc]). + + +decode_psk_identities(Identities) -> + decode_psk_identities(Identities, []). +%% +decode_psk_identities(<<>>, Acc) -> + lists:reverse(Acc); +decode_psk_identities(<<?UINT16(Len), Identity:Len/binary, Age:4/binary, Rest/binary>>, Acc) -> + decode_psk_identities(Rest, [#psk_identity{ + identity = Identity, + obfuscated_ticket_age = Age}|Acc]). + + +decode_psk_binders(Binders) -> + decode_psk_binders(Binders, []). +%% +decode_psk_binders(<<>>, Acc) -> + lists:reverse(Acc); +decode_psk_binders(<<?BYTE(Len), Binder:Len/binary, Rest/binary>>, Acc) -> + decode_psk_binders(Rest, [Binder|Acc]). + + %% encode/decode stream of certificate data to/from list of certificate data certs_to_list(ASN1Certs) -> certs_to_list(ASN1Certs, []). @@ -2658,7 +2786,7 @@ filter_unavailable_ecc_suites(_, Suites) -> handle_renegotiation_extension(Role, RecordCB, Version, Info, Random, NegotiatedCipherSuite, ClientCipherSuites, Compression, ConnectionStates0, Renegotiation, SecureRenegotation) -> - {ok, ConnectionStates} = handle_renegotiation_info(RecordCB, Role, Info, ConnectionStates0, + {ok, ConnectionStates} = handle_renegotiation_info(Version, RecordCB, Role, Info, ConnectionStates0, Renegotiation, SecureRenegotation, ClientCipherSuites), hello_pending_connection_states(RecordCB, Role, @@ -2928,11 +3056,11 @@ renegotiation_info(_RecordCB, server, ConnectionStates, true) -> #renegotiation_info{renegotiated_connection = undefined} end. -handle_renegotiation_info(_RecordCB, _, #renegotiation_info{renegotiated_connection = ?byte(0)}, +handle_renegotiation_info(_, _RecordCB, _, #renegotiation_info{renegotiated_connection = ?byte(0)}, ConnectionStates, false, _, _) -> {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)}; -handle_renegotiation_info(_RecordCB, server, undefined, ConnectionStates, _, _, CipherSuites) -> +handle_renegotiation_info(_, _RecordCB, server, undefined, ConnectionStates, _, _, CipherSuites) -> case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of true -> {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)}; @@ -2940,10 +3068,10 @@ handle_renegotiation_info(_RecordCB, server, undefined, ConnectionStates, _, _, {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)} end; -handle_renegotiation_info(_RecordCB, _, undefined, ConnectionStates, false, _, _) -> +handle_renegotiation_info(_, _RecordCB, _, undefined, ConnectionStates, false, _, _) -> {ok, ssl_record:set_renegotiation_flag(false, ConnectionStates)}; -handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_connection = ClientServerVerify}, +handle_renegotiation_info(_, _RecordCB, client, #renegotiation_info{renegotiated_connection = ClientServerVerify}, ConnectionStates, true, _, _) -> ConnectionState = ssl_record:current_connection_state(ConnectionStates, read), CData = maps:get(client_verify_data, ConnectionState), @@ -2954,7 +3082,7 @@ handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_co false -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, client_renegotiation)) end; -handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_connection = ClientVerify}, +handle_renegotiation_info(_, _RecordCB, server, #renegotiation_info{renegotiated_connection = ClientVerify}, ConnectionStates, true, _, CipherSuites) -> case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of @@ -2970,11 +3098,13 @@ handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_co throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, server_renegotiation)) end end; +handle_renegotiation_info({3,0}, _RecordCB, client, undefined, ConnectionStates, true, _SecureRenegotation, _) -> + {ok, ssl_record:set_renegotiation_flag(true, ConnectionStates)}; -handle_renegotiation_info(RecordCB, client, undefined, ConnectionStates, true, SecureRenegotation, _) -> +handle_renegotiation_info(_, RecordCB, client, undefined, ConnectionStates, true, SecureRenegotation, _) -> handle_renegotiation_info(RecordCB, ConnectionStates, SecureRenegotation); -handle_renegotiation_info(RecordCB, server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) -> +handle_renegotiation_info(_, RecordCB, server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) -> case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of true -> throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv})); @@ -3028,7 +3158,7 @@ empty_extensions({3,4}, client_hello) -> %% padding => undefined, key_share => undefined, pre_shared_key => undefined, - %% psk_key_exhange_modes => undefined, + psk_key_exchange_modes => undefined, %% early_data => undefined, %% cookie => undefined, client_hello_versions => undefined, @@ -3052,6 +3182,13 @@ empty_extensions({3,4}, server_hello) -> key_share => undefined, pre_shared_key => undefined }; +empty_extensions({3,4}, hello_retry_request) -> + #{server_hello_selected_version => undefined, + key_share => undefined, + pre_shared_key => undefined + }; +empty_extensions({3,0}, _) -> + empty_extensions(); empty_extensions(_, server_hello) -> #{renegotiation_info => undefined, alpn => undefined, diff --git a/lib/ssl/src/ssl_logger.erl b/lib/ssl/src/ssl_logger.erl index 987693b96b..514a4464bc 100644 --- a/lib/ssl/src/ssl_logger.erl +++ b/lib/ssl/src/ssl_logger.erl @@ -200,6 +200,11 @@ parse_handshake(Direction, #encrypted_extensions{} = EncryptedExtensions) -> Header = io_lib:format("~s Handshake, EncryptedExtensions", [header_prefix(Direction)]), Message = io_lib:format("~p", [?rec_info(encrypted_extensions, EncryptedExtensions)]), + {Header, Message}; +parse_handshake(Direction, #new_session_ticket{} = NewSessionTicket) -> + Header = io_lib:format("~s Post-Handshake, NewSessionTicket", + [header_prefix(Direction)]), + Message = io_lib:format("~p", [?rec_info(new_session_ticket, NewSessionTicket)]), {Header, Message}. diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index f317cb169e..3998f03519 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -31,6 +31,7 @@ -include("tls_connection.hrl"). -include("tls_handshake.hrl"). +-include("tls_handshake_1_3.hrl"). -include("ssl_alert.hrl"). -include("tls_record.hrl"). -include("ssl_cipher.hrl"). @@ -395,6 +396,7 @@ queue_handshake(Handshake, #state{handshake_env = #handshake_env{tls_handshake_h handshake_env = HsEnv#handshake_env{tls_handshake_history = Hist}, flight_buffer = Flight0 ++ [BinHandshake]}. + send_handshake_flight(#state{static_env = #static_env{socket = Socket, transport_cb = Transport}, flight_buffer = Flight} = State0) -> @@ -660,10 +662,16 @@ hello(internal, #server_hello{} = Hello, case tls_handshake:hello(Hello, SslOptions, ConnectionStates0, Renegotiation) of #alert{} = Alert -> %%TODO ssl_connection:handle_own_alert(Alert, ReqVersion, hello, - State#state{connection_env = CEnv#connection_env{negotiated_version = ReqVersion}}); + State#state{connection_env = + CEnv#connection_env{negotiated_version = ReqVersion}}); + %% Legacy TLS 1.2 and older {Version, NewId, ConnectionStates, ProtoExt, Protocol} -> ssl_connection:handle_session(Hello, - Version, NewId, ConnectionStates, ProtoExt, Protocol, State) + Version, NewId, ConnectionStates, ProtoExt, Protocol, State); + %% TLS 1.3 + {next_state, wait_sh} -> + %% Continue in TLS 1.3 'wait_sh' state + {next_state, wait_sh, State, [{next_event, internal, Hello}]} end; hello(info, Event, State) -> gen_info(Event, ?FUNCTION_NAME, State); @@ -804,6 +812,11 @@ connection(internal, #client_hello{}, State = reinit_handshake_data(State0), next_event(?FUNCTION_NAME, no_record, State); +connection(internal, #new_session_ticket{}, State) -> + %% TLS 1.3 + %% Drop NewSessionTicket (currently not supported) + next_event(?FUNCTION_NAME, no_record, State); + connection(Type, Event, State) -> ssl_connection:?FUNCTION_NAME(Type, Event, State, ?MODULE). @@ -1286,9 +1299,10 @@ maybe_generate_client_shares(#ssl_options{ versions = [Version|_], supported_groups = #supported_groups{ - supported_groups = Groups}}) + supported_groups = [Group|_]}}) when Version =:= {3,4} -> - ssl_cipher:generate_client_shares(Groups); + %% Generate only key_share entry for the most preferred group + ssl_cipher:generate_client_shares([Group]); maybe_generate_client_shares(_) -> undefined. diff --git a/lib/ssl/src/tls_connection_1_3.erl b/lib/ssl/src/tls_connection_1_3.erl index 76cdebc76f..117e4f059d 100644 --- a/lib/ssl/src/tls_connection_1_3.erl +++ b/lib/ssl/src/tls_connection_1_3.erl @@ -112,7 +112,10 @@ negotiated/4, wait_cert/4, wait_cv/4, - wait_finished/4 + wait_finished/4, + wait_sh/4, + wait_ee/4, + wait_cert_cr/4 ]). @@ -127,6 +130,13 @@ start(internal, #client_hello{} = Hello, State0, _Module) -> {State, negotiated} -> {next_state, negotiated, State, [{next_event, internal, start_handshake}]} end; +start(internal, #server_hello{} = ServerHello, State0, _Module) -> + case tls_handshake_1_3:do_start(ServerHello, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, start, State0); + {State, NextState} -> + {next_state, NextState, State, []} + end; start(Type, Msg, State, Connection) -> ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). @@ -183,3 +193,52 @@ wait_finished(internal, end; wait_finished(Type, Msg, State, Connection) -> ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). + + +wait_sh(internal, #change_cipher_spec{}, State, _Module) -> + tls_connection:next_event(?FUNCTION_NAME, no_record, State); +wait_sh(internal, #server_hello{} = Hello, State0, _Module) -> + case tls_handshake_1_3:do_wait_sh(Hello, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_sh, State0); + {State1, start, ServerHello} -> + %% hello_retry_request: go to start + {next_state, start, State1, [{next_event, internal, ServerHello}]}; + {State1, wait_ee} -> + tls_connection:next_event(wait_ee, no_record, State1) + end; +wait_sh(Type, Msg, State, Connection) -> + ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). + + +wait_ee(internal, #change_cipher_spec{}, State, _Module) -> + tls_connection:next_event(?FUNCTION_NAME, no_record, State); +wait_ee(internal, #encrypted_extensions{} = EE, State0, _Module) -> + case tls_handshake_1_3:do_wait_ee(EE, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_ee, State0); + {State1, NextState} -> + tls_connection:next_event(NextState, no_record, State1) + end; +wait_ee(Type, Msg, State, Connection) -> + ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). + + +wait_cert_cr(internal, #change_cipher_spec{}, State, _Module) -> + tls_connection:next_event(?FUNCTION_NAME, no_record, State); +wait_cert_cr(internal, #certificate_1_3{} = Certificate, State0, _Module) -> + case tls_handshake_1_3:do_wait_cert_cr(Certificate, State0) of + {#alert{} = Alert, State} -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_cert_cr, State); + {State1, NextState} -> + tls_connection:next_event(NextState, no_record, State1) + end; +wait_cert_cr(internal, #certificate_request_1_3{} = CertificateRequest, State0, _Module) -> + case tls_handshake_1_3:do_wait_cert_cr(CertificateRequest, State0) of + #alert{} = Alert -> + ssl_connection:handle_own_alert(Alert, {3,4}, wait_cert_cr, State0); + {State1, NextState} -> + tls_connection:next_event(NextState, no_record, State1) + end; +wait_cert_cr(Type, Msg, State, Connection) -> + ssl_connection:handle_common_event(Type, Msg, ?FUNCTION_NAME, State, Connection). diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 2480e05097..37265e0759 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -105,7 +105,7 @@ client_hello(Host, Port, ConnectionStates, {tls_record:tls_version(), {resumed | new, #session{}}, ssl_record:connection_states(), binary() | undefined, HelloExt::map(), {ssl:hash(), ssl:sign_algo()} | - undefined} | #alert{}. + undefined} | {atom(), atom()} |#alert{}. %% %% Description: Handles a received hello message %%-------------------------------------------------------------------- @@ -148,29 +148,48 @@ hello(#server_hello{server_version = {Major, Minor}, %% %% - If "supported_version" is present (ServerHello): %% - Abort handshake with an "illegal_parameter" alert -hello(#server_hello{server_version = Version, +hello(#server_hello{server_version = LegacyVersion, + random = Random, + cipher_suite = CipherSuite, + compression_method = Compression, + session_id = SessionId, extensions = #{server_hello_selected_version := - #server_hello_selected_version{selected_version = Version}} + #server_hello_selected_version{selected_version = Version} = HelloExt} }, - #ssl_options{versions = SupportedVersions}, - _ConnectionStates0, _Renegotiation) -> - case tls_record:is_higher({3,4}, Version) of + #ssl_options{versions = SupportedVersions} = SslOpt, + ConnectionStates0, Renegotiation) -> + %% In TLS 1.3, the TLS server indicates its version using the "supported_versions" extension + %% (Section 4.2.1), and the legacy_version field MUST be set to 0x0303, which is the version + %% number for TLS 1.2. + %% The "supported_versions" extension is supported from TLS 1.2. + case LegacyVersion > {3,3} orelse + LegacyVersion =:= {3,3} andalso Version < {3,3} of true -> ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); false -> case tls_record:is_acceptable_version(Version, SupportedVersions) of true -> - %% Implement TLS 1.3 statem ??? - ?ALERT_REC(?FATAL, ?PROTOCOL_VERSION); + case Version of + {3,3} -> + %% TLS 1.2 ServerHello with "supported_versions" (special case) + handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, + Compression, HelloExt, SslOpt, + ConnectionStates0, Renegotiation); + {3,4} -> + %% TLS 1.3 + {next_state, wait_sh} + end; false -> ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER) end end; -hello(#server_hello{server_version = Version, random = Random, +hello(#server_hello{server_version = Version, + random = Random, cipher_suite = CipherSuite, compression_method = Compression, - session_id = SessionId, extensions = HelloExt}, + session_id = SessionId, + extensions = HelloExt}, #ssl_options{versions = SupportedVersions} = SslOpt, ConnectionStates0, Renegotiation) -> case tls_record:is_acceptable_version(Version, SupportedVersions) of @@ -360,7 +379,7 @@ do_hello(Version, Versions, CipherSuites, Hello, SslOpts, Info, Renegotiation) - %%-------------------------------------------------------------------- enc_handshake(#hello_request{}, {3, N}) when N < 4 -> {?HELLO_REQUEST, <<>>}; -enc_handshake(#client_hello{client_version = {Major, Minor}, +enc_handshake(#client_hello{client_version = {Major, Minor} = Version, random = Random, session_id = SessionID, cipher_suites = CipherSuites, @@ -371,7 +390,7 @@ enc_handshake(#client_hello{client_version = {Major, Minor}, CmLength = byte_size(BinCompMethods), BinCipherSuites = list_to_binary(CipherSuites), CsLength = byte_size(BinCipherSuites), - ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions), + ExtensionsBin = ssl_handshake:encode_hello_extensions(HelloExtensions, Version), {?CLIENT_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, ?BYTE(SIDLength), SessionID/binary, diff --git a/lib/ssl/src/tls_handshake_1_3.erl b/lib/ssl/src/tls_handshake_1_3.erl index 53f9adbbd3..c29366e717 100644 --- a/lib/ssl/src/tls_handshake_1_3.erl +++ b/lib/ssl/src/tls_handshake_1_3.erl @@ -39,14 +39,23 @@ %% Create handshake messages -export([certificate/5, certificate_verify/4, - encrypted_extensions/0, - server_hello/4]). + encrypted_extensions/1]). -export([do_start/2, do_negotiated/2, do_wait_cert/2, do_wait_cv/2, - do_wait_finished/2]). + do_wait_finished/2, + do_wait_sh/2, + do_wait_ee/2, + do_wait_cert_cr/2]). + + +%% crypto:hash(sha256, "HelloRetryRequest"). +-define(HELLO_RETRY_REQUEST_RANDOM, <<207,33,173,116,229,154,97,17, + 190,29,140,2,30,101,184,145, + 194,162,17,22,122,187,140,94, + 7,158,9,226,200,168,51,156>>). %%==================================================================== %% Create handshake messages @@ -64,11 +73,24 @@ server_hello(MsgType, SessionId, KeyShare, ConnectionStates) -> extensions = Extensions }. + +%% The server's extensions MUST contain "supported_versions". +%% Additionally, it SHOULD contain the minimal set of extensions +%% necessary for the client to generate a correct ClientHello pair. As +%% with the ServerHello, a HelloRetryRequest MUST NOT contain any +%% extensions that were not first offered by the client in its +%% ClientHello, with the exception of optionally the "cookie" (see +%% Section 4.2.2) extension. +server_hello_extensions(hello_retry_request = MsgType, KeyShare) -> + SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, + Extensions = #{server_hello_selected_version => SupportedVersions}, + ssl_handshake:add_server_share(MsgType, Extensions, KeyShare); server_hello_extensions(MsgType, KeyShare) -> SupportedVersions = #server_hello_selected_version{selected_version = {3,4}}, Extensions = #{server_hello_selected_version => SupportedVersions}, ssl_handshake:add_server_share(MsgType, Extensions, KeyShare). + server_hello_random(server_hello, #security_parameters{server_random = Random}) -> Random; %% For reasons of backward compatibility with middleboxes (see @@ -79,13 +101,17 @@ server_hello_random(server_hello, #security_parameters{server_random = Random}) %% CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91 %% C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C server_hello_random(hello_retry_request, _) -> - crypto:hash(sha256, "HelloRetryRequest"). + ?HELLO_RETRY_REQUEST_RANDOM. -%% TODO: implement support for encrypted_extensions -encrypted_extensions() -> +encrypted_extensions(#state{handshake_env = #handshake_env{alpn = undefined}}) -> #encrypted_extensions{ extensions = #{} + }; +encrypted_extensions(#state{handshake_env = #handshake_env{alpn = ALPNProtocol}}) -> + Extensions = ssl_handshake:add_alpn(#{}, ALPNProtocol), + #encrypted_extensions{ + extensions = Extensions }. @@ -119,7 +145,6 @@ filter_tls13_algs(Algo) -> lists:filter(fun is_atom/1, Algo). -%% TODO: use maybe monad for error handling! %% enum { %% X509(0), %% RawPublicKey(2), @@ -142,18 +167,28 @@ filter_tls13_algs(Algo) -> %% opaque certificate_request_context<0..2^8-1>; %% CertificateEntry certificate_list<0..2^24-1>; %% } Certificate; -certificate(OwnCert, CertDbHandle, CertDbRef, _CRContext, server) -> +certificate(OwnCert, CertDbHandle, CertDbRef, _CRContext, Role) -> case ssl_certificate:certificate_chain(OwnCert, CertDbHandle, CertDbRef) of {ok, _, Chain} -> CertList = chain_to_cert_list(Chain), %% If this message is in response to a CertificateRequest, the value of %% certificate_request_context in that message. Otherwise (in the case %%of server authentication), this field SHALL be zero length. - #certificate_1_3{ - certificate_request_context = <<>>, - certificate_list = CertList}; - {error, Error} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {server_has_no_suitable_certificates, Error}) + {ok, #certificate_1_3{ + certificate_request_context = <<>>, + certificate_list = CertList}}; + {error, Error} when Role =:= server -> + {error, {no_suitable_certificates, Error}}; + {error, _Error} when Role =:= client -> + %% The client MUST send a Certificate message if and only if the server + %% has requested client authentication via a CertificateRequest message + %% (Section 4.3.2). If the server requests client authentication but no + %% suitable certificate is available, the client MUST send a Certificate + %% message containing no certificates (i.e., with the "certificate_list" + %% field having length 0). + {ok, #certificate_1_3{ + certificate_request_context = <<>>, + certificate_list = []}} end. @@ -161,7 +196,7 @@ certificate_verify(PrivateKey, SignatureScheme, #state{connection_states = ConnectionStates, handshake_env = #handshake_env{ - tls_handshake_history = {Messages, _}}}, server) -> + tls_handshake_history = {Messages, _}}}, Role) -> #{security_parameters := SecParamsR} = ssl_record:pending_connection_state(ConnectionStates, write), #security_parameters{prf_algorithm = HKDFAlgo} = SecParamsR, @@ -173,11 +208,11 @@ certificate_verify(PrivateKey, SignatureScheme, %% Transcript-Hash uses the HKDF hash function defined by the cipher suite. THash = tls_v1:transcript_hash(Context, HKDFAlgo), + ContextString = context_string(Role), %% Digital signatures use the hash function defined by the selected signature %% scheme. - case sign(THash, <<"TLS 1.3, server CertificateVerify">>, - HashAlgo, PrivateKey) of + case sign(THash, ContextString, HashAlgo, PrivateKey) of {ok, Signature} -> {ok, #certificate_verify_1_3{ algorithm = SignatureScheme, @@ -252,6 +287,21 @@ encode_handshake(HandshakeMsg) -> %% Decode handshake %%==================================================================== + +decode_handshake(?SERVER_HELLO, <<?BYTE(Major), ?BYTE(Minor), Random:32/binary, + ?BYTE(SID_length), Session_ID:SID_length/binary, + Cipher_suite:2/binary, ?BYTE(Comp_method), + ?UINT16(ExtLen), Extensions:ExtLen/binary>>) + when Random =:= ?HELLO_RETRY_REQUEST_RANDOM -> + HelloExtensions = ssl_handshake:decode_hello_extensions(Extensions, {3,4}, {Major, Minor}, + hello_retry_request), + #server_hello{ + server_version = {Major,Minor}, + random = Random, + session_id = Session_ID, + cipher_suite = Cipher_suite, + compression_method = Comp_method, + extensions = HelloExtensions}; decode_handshake(?CERTIFICATE_REQUEST, <<?BYTE(0), ?UINT16(Size), EncExts:Size/binary>>) -> Exts = decode_extensions(EncExts, certificate_request), #certificate_request_1_3{ @@ -384,6 +434,15 @@ certificate_entry(DER) -> %% 79 %% 00 %% 0101010101010101010101010101010101010101010101010101010101010101 +sign(THash, Context, HashAlgo, #'ECPrivateKey'{} = PrivateKey) -> + Content = build_content(Context, THash), + try public_key:sign(Content, HashAlgo, PrivateKey) of + Signature -> + {ok, Signature} + catch + error:badarg -> + {error, badarg} + end; sign(THash, Context, HashAlgo, PrivateKey) -> Content = build_content(Context, THash), @@ -401,7 +460,16 @@ sign(THash, Context, HashAlgo, PrivateKey) -> end. -verify(THash, Context, HashAlgo, Signature, PublicKey) -> +verify(THash, Context, HashAlgo, Signature, {?'id-ecPublicKey', PublicKey, PublicKeyParams}) -> + Content = build_content(Context, THash), + try public_key:verify(Content, HashAlgo, Signature, {PublicKey, PublicKeyParams}) of + Result -> + {ok, Result} + catch + error:badarg -> + {error, badarg} + end; +verify(THash, Context, HashAlgo, Signature, {?rsaEncryption, PublicKey, _PubKeyParams}) -> Content = build_content(Context, THash), %% The length of the Salt MUST be equal to the length of the output @@ -428,15 +496,17 @@ build_content(Context, THash) -> %%==================================================================== +%% TLS Server do_start(#client_hello{cipher_suites = ClientCiphers, session_id = SessionId, extensions = Extensions} = _Hello, #state{connection_states = _ConnectionStates0, ssl_options = #ssl_options{ciphers = ServerCiphers, signature_algs = ServerSignAlgs, - supported_groups = ServerGroups0}, + supported_groups = ServerGroups0, + alpn_preferred_protocols = ALPNPreferredProtocols, + honor_cipher_order = HonorCipherOrder}, session = #session{own_certificate = Cert}} = State0) -> - ClientGroups0 = maps:get(elliptic_curves, Extensions, undefined), ClientGroups = get_supported_groups(ClientGroups0), ServerGroups = get_supported_groups(ServerGroups0), @@ -444,23 +514,27 @@ do_start(#client_hello{cipher_suites = ClientCiphers, ClientShares0 = maps:get(key_share, Extensions, undefined), ClientShares = get_key_shares(ClientShares0), + ClientALPN0 = maps:get(alpn, Extensions, undefined), + ClientALPN = ssl_handshake:decode_alpn(ClientALPN0), + ClientSignAlgs = get_signature_scheme_list( maps:get(signature_algs, Extensions, undefined)), ClientSignAlgsCert = get_signature_scheme_list( maps:get(signature_algs_cert, Extensions, undefined)), - %% TODO: use library function if it exists - %% Init the maybe "monad" {Ref,Maybe} = maybe(), try + %% Handle ALPN extension if ALPN is configured + ALPNProtocol = Maybe(handle_alpn(ALPNPreferredProtocols, ClientALPN)), + %% If the server does not select a PSK, then the server independently selects a %% cipher suite, an (EC)DHE group and key share for key establishment, %% and a signature algorithm/certificate pair to authenticate itself to %% the client. - Cipher = Maybe(select_cipher_suite(ClientCiphers, ServerCiphers)), + Cipher = Maybe(select_cipher_suite(HonorCipherOrder, ClientCiphers, ServerCiphers)), Groups = Maybe(select_common_groups(ServerGroups, ClientGroups)), - Maybe(validate_key_share(ClientGroups, ClientShares)), + Maybe(validate_client_key_share(ClientGroups, ClientShares)), {PublicKeyAlgo, SignAlgo, SignHash} = get_certificate_params(Cert), @@ -479,8 +553,14 @@ do_start(#client_hello{cipher_suites = ClientCiphers, %% Generate server_share KeyShare = ssl_cipher:generate_server_share(Group), - State1 = update_start_state(State0, Cipher, KeyShare, SessionId, - Group, SelectedSignAlg, ClientPubKey), + State1 = update_start_state(State0, + #{cipher => Cipher, + key_share => KeyShare, + session_id => SessionId, + group => Group, + sign_alg => SelectedSignAlg, + peer_public_key => ClientPubKey, + alpn => ALPNProtocol}), %% 4.1.4. Hello Retry Request %% @@ -490,10 +570,7 @@ do_start(#client_hello{cipher_suites = ClientCiphers, %% the handshake. Maybe(send_hello_retry_request(State1, ClientPubKey, KeyShare, SessionId)) - %% TODO: - %% - session handling - %% - handle extensions: ALPN - %% (do not handle: NPN, srp, renegotiation_info, ec_point_formats) + %% TODO: session handling catch {Ref, {insufficient_security, no_suitable_groups}} -> @@ -505,7 +582,87 @@ do_start(#client_hello{cipher_suites = ClientCiphers, {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); {Ref, {insufficient_security, no_suitable_public_key}} -> - ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key); + {Ref, no_application_protocol} -> + ?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL) + end; +%% TLS Client +do_start(#server_hello{cipher_suite = SelectedCipherSuite, + session_id = SessionId, + extensions = Extensions} = _ServerHello, + #state{static_env = #static_env{role = client, + host = Host, + port = Port, + transport_cb = Transport, + socket = Socket, + session_cache = Cache, + session_cache_cb = CacheCb}, + handshake_env = #handshake_env{renegotiation = {Renegotiation, _}, + tls_handshake_history = _HHistory} = HsEnv, + connection_env = CEnv, + ssl_options = #ssl_options{ciphers = ClientCiphers, + supported_groups = ClientGroups0} = SslOpts, + session = #session{own_certificate = Cert} = Session0, + connection_states = ConnectionStates0 + } = State0) -> + ClientGroups = get_supported_groups(ClientGroups0), + + {Ref,Maybe} = maybe(), + try + ServerKeyShare = maps:get(key_share, Extensions, undefined), + SelectedGroup = get_selected_group(ServerKeyShare), + + %% Upon receipt of this extension in a HelloRetryRequest, the client + %% MUST verify that (1) the selected_group field corresponds to a group + %% which was provided in the "supported_groups" extension in the + %% original ClientHello and (2) the selected_group field does not + %% correspond to a group which was provided in the "key_share" extension + %% in the original ClientHello. If either of these checks fails, then + %% the client MUST abort the handshake with an "illegal_parameter" + %% alert. + Maybe(validate_selected_group(SelectedGroup, ClientGroups)), + + Maybe(validate_cipher_suite(SelectedCipherSuite, ClientCiphers)), + + %% Otherwise, when sending the new ClientHello, the client MUST + %% replace the original "key_share" extension with one containing only a + %% new KeyShareEntry for the group indicated in the selected_group field + %% of the triggering HelloRetryRequest. + ClientKeyShare = ssl_cipher:generate_client_shares([SelectedGroup]), + Hello = tls_handshake:client_hello(Host, Port, ConnectionStates0, SslOpts, + Cache, CacheCb, Renegotiation, Cert, ClientKeyShare), + + HelloVersion = tls_record:hello_version(SslOpts#ssl_options.versions), + + %% Update state + State1 = update_start_state(State0, + #{cipher => SelectedCipherSuite, + key_share => ClientKeyShare, + session_id => SessionId, + group => SelectedGroup}), + + %% Replace ClientHello1 with a special synthetic handshake message + State2 = replace_ch1_with_message_hash(State1), + #state{handshake_env = #handshake_env{tls_handshake_history = HHistory}} = State2, + + {BinMsg, ConnectionStates, Handshake} = + tls_connection:encode_handshake(Hello, HelloVersion, ConnectionStates0, HHistory), + tls_socket:send(Transport, Socket, BinMsg), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'handshake', Hello), + ssl_logger:debug(SslOpts#ssl_options.log_level, outbound, 'record', BinMsg), + + State = State2#state{ + connection_states = ConnectionStates, + connection_env = CEnv#connection_env{negotiated_version = HelloVersion}, %% Requested version + session = Session0#session{session_id = Hello#client_hello.session_id}, + handshake_env = HsEnv#handshake_env{tls_handshake_history = Handshake}, + key_share = ClientKeyShare}, + + {State, wait_sh} + + catch + {Ref, {illegal_parameter, Reason}} -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, Reason) end. @@ -515,7 +672,7 @@ do_negotiated(start_handshake, own_certificate = OwnCert, ecc = SelectedGroup, sign_alg = SignatureScheme, - dh_public_value = ClientKey}, + dh_public_value = ClientPublicKey}, ssl_options = #ssl_options{} = SslOpts, key_share = KeyShare, handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, @@ -526,6 +683,8 @@ do_negotiated(start_handshake, socket = _Socket, transport_cb = _Transport} } = State0) -> + ServerPrivateKey = get_server_private_key(KeyShare), + {Ref,Maybe} = maybe(), try @@ -536,12 +695,12 @@ do_negotiated(start_handshake, {State1, _} = tls_connection:send_handshake(ServerHello, State0), State2 = - calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, State1), + calculate_handshake_secrets(ClientPublicKey, ServerPrivateKey, SelectedGroup, State1), State3 = ssl_record:step_encryption_state(State2), %% Create EncryptedExtensions - EncryptedExtensions = encrypted_extensions(), + EncryptedExtensions = encrypted_extensions(State2), %% Encode EncryptedExtensions State4 = tls_connection:queue_handshake(EncryptedExtensions, State3), @@ -550,7 +709,7 @@ do_negotiated(start_handshake, {State5, NextState} = maybe_send_certificate_request(State4, SslOpts), %% Create Certificate - Certificate = certificate(OwnCert, CertDbHandle, CertDbRef, <<>>, server), + Certificate = Maybe(certificate(OwnCert, CertDbHandle, CertDbRef, <<>>, server)), %% Encode Certificate State6 = tls_connection:queue_handshake(Certificate, State5), @@ -574,14 +733,16 @@ do_negotiated(start_handshake, catch {Ref, badarg} -> - ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {digitally_sign, badarg}) + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {digitally_sign, badarg}); + {Ref, {no_suitable_certificates, Reason}} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {no_suitable_certificates, Reason}) end. do_wait_cert(#certificate_1_3{} = Certificate, State0) -> {Ref,Maybe} = maybe(), try - Maybe(process_client_certificate(Certificate, State0)) + Maybe(process_certificate(Certificate, State0)) catch {Ref, {certificate_required, State}} -> {?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required), State}; @@ -591,6 +752,8 @@ do_wait_cert(#certificate_1_3{} = Certificate, State0) -> {?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason), State}; {Ref, {{handshake_failure, Reason}, State}} -> {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason), State}; + {Ref, {#alert{} = Alert, State}} -> + {Alert, State}; {#alert{} = Alert, State} -> {Alert, State} end. @@ -599,8 +762,8 @@ do_wait_cert(#certificate_1_3{} = Certificate, State0) -> do_wait_cv(#certificate_verify_1_3{} = CertificateVerify, State0) -> {Ref,Maybe} = maybe(), try - Maybe(verify_signature_algorithm(State0, CertificateVerify)), - Maybe(verify_certificate_verify(State0, CertificateVerify)) + State1 = Maybe(verify_signature_algorithm(State0, CertificateVerify)), + Maybe(verify_certificate_verify(State1, CertificateVerify)) catch {Ref, {{bad_certificate, Reason}, State}} -> {?ALERT_REC(?FATAL, ?BAD_CERTIFICATE, {bad_certificate, Reason}), State}; @@ -610,20 +773,9 @@ do_wait_cv(#certificate_verify_1_3{} = CertificateVerify, State0) -> {?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {handshake_failure, Reason}), State} end. - +%% TLS Server do_wait_finished(#finished{verify_data = VerifyData}, - #state{connection_states = _ConnectionStates0, - session = #session{session_id = _SessionId, - own_certificate = _OwnCert}, - ssl_options = #ssl_options{} = _SslOpts, - key_share = _KeyShare, - handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, - static_env = #static_env{ - cert_db = _CertDbHandle, - cert_db_ref = _CertDbRef, - socket = _Socket, - transport_cb = _Transport} - } = State0) -> + #state{static_env = #static_env{role = server}} = State0) -> {Ref,Maybe} = maybe(), @@ -639,19 +791,237 @@ do_wait_finished(#finished{verify_data = VerifyData}, catch {Ref, decrypt_error} -> ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error) + end; +%% TLS Client +do_wait_finished(#finished{verify_data = _VerifyData}, + #state{static_env = #static_env{role = client}} = State0) -> + + {Ref,Maybe} = maybe(), + + try + %% Maybe(validate_client_finished(State0, VerifyData)), + + %% Maybe send Certificate + CertificateVerify + State1 = Maybe(maybe_queue_cert_cert_cv(State0)), + + Finished = finished(State1), + + %% Encode Finished + State2 = tls_connection:queue_handshake(Finished, State1), + + %% Send first flight + {State3, _} = tls_connection:send_handshake_flight(State2), + + State4 = calculate_traffic_secrets(State3), + + %% Configure traffic keys + ssl_record:step_encryption_state(State4) + + catch + {Ref, decrypt_error} -> + ?ALERT_REC(?FATAL, ?DECRYPT_ERROR, decrypt_error); + {Ref, badarg} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {digitally_sign, badarg}); + {Ref, {no_suitable_certificates, Reason}} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, {no_suitable_certificates, Reason}) + end. + + +do_wait_sh(#server_hello{cipher_suite = SelectedCipherSuite, + session_id = SessionId, + extensions = Extensions} = ServerHello, + #state{key_share = ClientKeyShare0, + ssl_options = #ssl_options{ciphers = ClientCiphers, + supported_groups = ClientGroups0}} = State0) -> + ClientGroups = get_supported_groups(ClientGroups0), + ServerKeyShare0 = maps:get(key_share, Extensions, undefined), + ClientKeyShare = get_key_shares(ClientKeyShare0), + + {Ref,Maybe} = maybe(), + try + %% Go to state 'start' if server replies with 'HelloRetryRequest'. + Maybe(maybe_hello_retry_request(ServerHello, State0)), + + ServerKeyShare = get_key_shares(ServerKeyShare0), + + Maybe(validate_cipher_suite(SelectedCipherSuite, ClientCiphers)), + Maybe(validate_server_key_share(ClientGroups, ServerKeyShare)), + + %% Get server public key + {SelectedGroup, ServerPublicKey} = get_server_public_key(ServerKeyShare), + + {_, ClientPrivateKey} = get_client_private_key([SelectedGroup], ClientKeyShare), + + %% Update state + State1 = update_start_state(State0, + #{cipher => SelectedCipherSuite, + key_share => ClientKeyShare0, + session_id => SessionId, + group => SelectedGroup, + peer_public_key => ServerPublicKey}), + + State2 = calculate_handshake_secrets(ServerPublicKey, ClientPrivateKey, SelectedGroup, State1), + + State3 = ssl_record:step_encryption_state(State2), + + {State3, wait_ee} + + catch + {Ref, {State, StateName, ServerHello}} -> + {State, StateName, ServerHello}; + {Ref, {insufficient_security, no_suitable_groups}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); + {Ref, illegal_parameter} -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + {Ref, no_suitable_cipher} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); + {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); + {Ref, {insufficient_security, no_suitable_public_key}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) + end. + + +do_wait_ee(#encrypted_extensions{extensions = Extensions}, State0) -> + + ALPNProtocol0 = maps:get(alpn, Extensions, undefined), + ALPNProtocol = get_alpn(ALPNProtocol0), + + {Ref,_Maybe} = maybe(), + + try + %% Update state + #state{handshake_env = HsEnv} = State0, + State1 = State0#state{handshake_env = HsEnv#handshake_env{alpn = ALPNProtocol}}, + + {State1, wait_cert_cr} + catch + {Ref, {insufficient_security, no_suitable_groups}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_groups); + {Ref, illegal_parameter} -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER); + {Ref, no_suitable_cipher} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_cipher); + {Ref, {insufficient_security, no_suitable_signature_algorithm}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, "No suitable signature algorithm"); + {Ref, {insufficient_security, no_suitable_public_key}} -> + ?ALERT_REC(?FATAL, ?INSUFFICIENT_SECURITY, no_suitable_public_key) end. +do_wait_cert_cr(#certificate_1_3{} = Certificate, State0) -> + {Ref,Maybe} = maybe(), + try + Maybe(process_certificate(Certificate, State0)) + catch + {Ref, {certificate_required, _State}} -> + ?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required); + {Ref, {{certificate_unknown, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, Reason); + {Ref, {{internal_error, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason); + {Ref, {{handshake_failure, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason); + {Ref, {#alert{} = Alert, State}} -> + {Alert, State} + end; +do_wait_cert_cr(#certificate_request_1_3{} = CertificateRequest, State0) -> + {Ref,Maybe} = maybe(), + try + Maybe(process_certificate_request(CertificateRequest, State0)) + catch + {Ref, {certificate_required, _State}} -> + ?ALERT_REC(?FATAL, ?CERTIFICATE_REQUIRED, certificate_required); + {Ref, {{certificate_unknown, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?CERTIFICATE_UNKNOWN, Reason); + {Ref, {illegal_parameter, Reason}} -> + ?ALERT_REC(?FATAL, ?ILLEGAL_PARAMETER, Reason); + {Ref, {{internal_error, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?INTERNAL_ERROR, Reason); + {Ref, {{handshake_failure, Reason}, _State}} -> + ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason) + end. + + + %% TODO: Remove this function! %% not_implemented(State, Reason) -> %% {error, {not_implemented, State, Reason}}. -%% + %% not_implemented(update_secrets, State0, Reason) -> %% State1 = calculate_traffic_secrets(State0), %% State = ssl_record:step_encryption_state(State1), %% {error, {not_implemented, State, Reason}}. +%% For reasons of backward compatibility with middleboxes (see +%% Appendix D.4), the HelloRetryRequest message uses the same structure +%% as the ServerHello, but with Random set to the special value of the +%% SHA-256 of "HelloRetryRequest": +%% +%% CF 21 AD 74 E5 9A 61 11 BE 1D 8C 02 1E 65 B8 91 +%% C2 A2 11 16 7A BB 8C 5E 07 9E 09 E2 C8 A8 33 9C +%% +%% Upon receiving a message with type server_hello, implementations MUST +%% first examine the Random value and, if it matches this value, process +%% it as described in Section 4.1.4). +maybe_hello_retry_request(#server_hello{random = ?HELLO_RETRY_REQUEST_RANDOM} = ServerHello, State0) -> + {error, {State0, start, ServerHello}}; +maybe_hello_retry_request(_, _) -> + ok. + + +maybe_queue_cert_cert_cv(#state{client_certificate_requested = false} = State) -> + {ok, State}; +maybe_queue_cert_cert_cv(#state{connection_states = _ConnectionStates0, + session = #session{session_id = _SessionId, + own_certificate = OwnCert}, + ssl_options = #ssl_options{} = _SslOpts, + key_share = _KeyShare, + handshake_env = #handshake_env{tls_handshake_history = _HHistory0}, + static_env = #static_env{ + role = client, + cert_db = CertDbHandle, + cert_db_ref = CertDbRef, + socket = _Socket, + transport_cb = _Transport} + } = State0) -> + {Ref,Maybe} = maybe(), + try + %% Create Certificate + Certificate = Maybe(certificate(OwnCert, CertDbHandle, CertDbRef, <<>>, client)), + + %% Encode Certificate + State1 = tls_connection:queue_handshake(Certificate, State0), + + %% Maybe create and queue CertificateVerify + State = Maybe(maybe_queue_cert_verify(Certificate, State1)), + {ok, State} + catch + {Ref, badarg} -> + {error, badarg} + end. + + +%% Clients MUST send this message whenever authenticating via a certificate +%% (i.e., when the Certificate message is non-empty). +maybe_queue_cert_verify(#certificate_1_3{certificate_list = []}, State) -> + {ok, State}; +maybe_queue_cert_verify(_Certificate, + #state{connection_states = _ConnectionStates0, + session = #session{sign_alg = SignatureScheme}, + connection_env = #connection_env{private_key = CertPrivateKey}, + static_env = #static_env{role = client} + } = State) -> + {Ref,Maybe} = maybe(), + try + CertificateVerify = Maybe(certificate_verify(CertPrivateKey, SignatureScheme, State, client)), + {ok, tls_connection:queue_handshake(CertificateVerify, State)} + catch + {Ref, badarg} -> + {error, badarg} + end. + %% Recipients of Finished messages MUST verify that the contents are %% correct and if incorrect MUST terminate the connection with a @@ -684,7 +1054,7 @@ send_hello_retry_request(#state{connection_states = ConnectionStates0} = State0, ServerHello = server_hello(hello_retry_request, SessionId, KeyShare, ConnectionStates0), {State1, _} = tls_connection:send_handshake(ServerHello, State0), - %% TODO: Fix handshake history! + %% Update handshake history State2 = replace_ch1_with_message_hash(State1), {ok, {State2, start}}; @@ -703,19 +1073,44 @@ maybe_send_certificate_request(State, #ssl_options{ {tls_connection:queue_handshake(CertificateRequest, State), wait_cert}. -process_client_certificate(#certificate_1_3{ - certificate_request_context = <<>>, - certificate_list = []}, - #state{ssl_options = - #ssl_options{ - fail_if_no_peer_cert = false}} = State) -> +process_certificate_request(#certificate_request_1_3{}, + #state{session = #session{own_certificate = undefined}} = State) -> + {ok, {State#state{client_certificate_requested = true}, wait_cert}}; + +process_certificate_request(#certificate_request_1_3{ + extensions = Extensions}, + #state{session = #session{own_certificate = Cert} = Session} = State) -> + ServerSignAlgs = get_signature_scheme_list( + maps:get(signature_algs, Extensions, undefined)), + ServerSignAlgsCert = get_signature_scheme_list( + maps:get(signature_algs_cert, Extensions, undefined)), + + {_PublicKeyAlgo, SignAlgo, SignHash} = get_certificate_params(Cert), + + %% Check if server supports signature algorithm of client certificate + case check_cert_sign_algo(SignAlgo, SignHash, ServerSignAlgs, ServerSignAlgsCert) of + ok -> + {ok, {State#state{client_certificate_requested = true}, wait_cert}}; + {error, _} -> + %% Certificate not supported: send empty certificate in state 'wait_finished' + {ok, {State#state{client_certificate_requested = true, + session = Session#session{own_certificate = undefined}}, wait_cert}} + end. + + +process_certificate(#certificate_1_3{ + certificate_request_context = <<>>, + certificate_list = []}, + #state{ssl_options = + #ssl_options{ + fail_if_no_peer_cert = false}} = State) -> {ok, {State, wait_finished}}; -process_client_certificate(#certificate_1_3{ - certificate_request_context = <<>>, - certificate_list = []}, - #state{ssl_options = - #ssl_options{ - fail_if_no_peer_cert = true}} = State0) -> +process_certificate(#certificate_1_3{ + certificate_request_context = <<>>, + certificate_list = []}, + #state{ssl_options = + #ssl_options{ + fail_if_no_peer_cert = true}} = State0) -> %% At this point the client believes that the connection is up and starts using %% its traffic secrets. In order to be able send an proper Alert to the client @@ -724,19 +1119,18 @@ process_client_certificate(#certificate_1_3{ State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), {error, {certificate_required, State}}; -process_client_certificate(#certificate_1_3{certificate_list = Certs0}, - #state{ssl_options = - #ssl_options{signature_algs = SignAlgs, - signature_algs_cert = SignAlgsCert} = SslOptions, - static_env = - #static_env{ - role = Role, - host = Host, - cert_db = CertDbHandle, - cert_db_ref = CertDbRef, - crl_db = CRLDbHandle}} = State0) -> +process_certificate(#certificate_1_3{certificate_list = Certs0}, + #state{ssl_options = + #ssl_options{signature_algs = SignAlgs, + signature_algs_cert = SignAlgsCert} = SslOptions, + static_env = + #static_env{ + role = Role, + host = Host, + cert_db = CertDbHandle, + cert_db_ref = CertDbRef, + crl_db = CRLDbHandle}} = State0) -> %% TODO: handle extensions! - %% Remove extensions from list of certificates! Certs = convert_certificate_chain(Certs0), case is_supported_signature_algorithm(Certs, SignAlgs, SignAlgsCert) of @@ -747,13 +1141,11 @@ process_client_certificate(#certificate_1_3{certificate_list = Certs0}, State = store_peer_cert(State0, PeerCert, PublicKeyInfo), {ok, {State, wait_cv}}; {error, Reason} -> - State1 = calculate_traffic_secrets(State0), - State = ssl_record:step_encryption_state(State1), + State = update_encryption_state(Role, State0), {error, {Reason, State}}; - #alert{} = Alert -> - State1 = calculate_traffic_secrets(State0), - State = ssl_record:step_encryption_state(State1), - {Alert, State} + {ok, #alert{} = Alert} -> + State = update_encryption_state(Role, State0), + {error, {Alert, State}} end; false -> State1 = calculate_traffic_secrets(State0), @@ -777,6 +1169,17 @@ is_supported_signature_algorithm([BinCert|_], SignAlgs0) -> lists:member(Scheme, SignAlgs). +%% Sets correct encryption state when sending Alerts in shared states that use different secrets. +%% - If client: use handshake secrets. +%% - If server: use traffic secrets as by this time the client's state machine +%% already stepped into the 'connection' state. +update_encryption_state(server, State0) -> + State1 = calculate_traffic_secrets(State0), + ssl_record:step_encryption_state(State1); +update_encryption_state(client, State) -> + State. + + validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHandle, Role, Host) -> ServerName = ssl_handshake:server_name(SslOptions#ssl_options.server_name_indication, Host, Role), [PeerCert | ChainCerts ] = Certs, @@ -797,9 +1200,9 @@ validate_certificate_chain(Certs, CertDbHandle, CertDbRef, SslOptions, CRLDbHand {ok, {PublicKeyInfo,_}} -> {ok, {PeerCert, PublicKeyInfo}}; {error, Reason} -> - ssl_handshake:handle_path_validation_error(Reason, PeerCert, ChainCerts, - SslOptions, Options, - CertDbHandle, CertDbRef) + {ok, ssl_handshake:handle_path_validation_error(Reason, PeerCert, ChainCerts, + SslOptions, Options, + CertDbHandle, CertDbRef)} end catch error:{badmatch,{error, {asn1, Asn1Reason}}} -> @@ -861,7 +1264,7 @@ message_hash(ClientHello1, HKDFAlgo) -> crypto:hash(HKDFAlgo, ClientHello1)]. -calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, +calculate_handshake_secrets(PublicKey, PrivateKey, SelectedGroup, #state{connection_states = ConnectionStates, handshake_env = #handshake_env{ @@ -874,13 +1277,13 @@ calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, %% Calculate handshake_secret PSK = binary:copy(<<0>>, ssl_cipher:hash_size(HKDFAlgo)), EarlySecret = tls_v1:key_schedule(early_secret, HKDFAlgo , {psk, PSK}), - PrivateKey = get_server_private_key(KeyShare), %% #'ECPrivateKey'{} - IKM = calculate_shared_secret(ClientKey, PrivateKey, SelectedGroup), + IKM = calculate_shared_secret(PublicKey, PrivateKey, SelectedGroup), HandshakeSecret = tls_v1:key_schedule(handshake_secret, HKDFAlgo, IKM, EarlySecret), %% Calculate [sender]_handshake_traffic_secret {Messages, _} = HHistory, + ClientHSTrafficSecret = tls_v1:client_handshake_traffic_secret(HKDFAlgo, HandshakeSecret, lists:reverse(Messages)), ServerHSTrafficSecret = @@ -899,10 +1302,13 @@ calculate_handshake_secrets(ClientKey, SelectedGroup, KeyShare, ReadKey, ReadIV, ReadFinishedKey, WriteKey, WriteIV, WriteFinishedKey). -calculate_traffic_secrets(#state{connection_states = ConnectionStates, - handshake_env = - #handshake_env{ - tls_handshake_history = HHistory}} = State0) -> + +calculate_traffic_secrets(#state{ + static_env = #static_env{role = Role}, + connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + tls_handshake_history = HHistory}} = State0) -> #{security_parameters := SecParamsR} = ssl_record:pending_connection_state(ConnectionStates, read), #security_parameters{prf_algorithm = HKDFAlgo, @@ -913,7 +1319,7 @@ calculate_traffic_secrets(#state{connection_states = ConnectionStates, tls_v1:key_schedule(master_secret, HKDFAlgo, HandshakeSecret), %% Get the correct list messages for the handshake context. - Messages = get_handshake_context(HHistory), + Messages = get_handshake_context(Role, HHistory), %% Calculate [sender]_application_traffic_secret_0 ClientAppTrafficSecret0 = @@ -942,11 +1348,6 @@ get_private_key(#key_share_entry{ {_, PrivateKey}}) -> PrivateKey. -%% TODO: implement EC keys -get_public_key({?'rsaEncryption', PublicKey, _}) -> - PublicKey. - - %% X25519, X448 calculate_shared_secret(OthersKey, MyKey, Group) when is_binary(OthersKey) andalso is_binary(MyKey) andalso @@ -966,9 +1367,11 @@ calculate_shared_secret(OthersKey, MyKey = #'ECPrivateKey'{}, _Group) public_key:compute_key(Point, MyKey). -update_pending_connection_states(#state{connection_states = - CS = #{pending_read := PendingRead0, - pending_write := PendingWrite0}} = State, +update_pending_connection_states(#state{ + static_env = #static_env{role = server}, + connection_states = + CS = #{pending_read := PendingRead0, + pending_write := PendingWrite0}} = State, HandshakeSecret, ReadKey, ReadIV, ReadFinishedKey, WriteKey, WriteIV, WriteFinishedKey) -> @@ -977,8 +1380,23 @@ update_pending_connection_states(#state{connection_states = PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, WriteKey, WriteIV, WriteFinishedKey), State#state{connection_states = CS#{pending_read => PendingRead, + pending_write => PendingWrite}}; +update_pending_connection_states(#state{ + static_env = #static_env{role = client}, + connection_states = + CS = #{pending_read := PendingRead0, + pending_write := PendingWrite0}} = State, + HandshakeSecret, + ReadKey, ReadIV, ReadFinishedKey, + WriteKey, WriteIV, WriteFinishedKey) -> + PendingRead = update_connection_state(PendingRead0, HandshakeSecret, + WriteKey, WriteIV, WriteFinishedKey), + PendingWrite = update_connection_state(PendingWrite0, HandshakeSecret, + ReadKey, ReadIV, ReadFinishedKey), + State#state{connection_states = CS#{pending_read => PendingRead, pending_write => PendingWrite}}. + update_connection_state(ConnectionState = #{security_parameters := SecurityParameters0}, HandshakeSecret, Key, IV, FinishedKey) -> %% Store secret @@ -988,11 +1406,24 @@ update_connection_state(ConnectionState = #{security_parameters := SecurityParam cipher_state => cipher_init(Key, IV, FinishedKey)}. +update_start_state(State, Map) -> + Cipher = maps:get(cipher, Map, undefined), + KeyShare = maps:get(key_share, Map, undefined), + SessionId = maps:get(session_id, Map, undefined), + Group = maps:get(group, Map, undefined), + SelectedSignAlg = maps:get(sign_alg, Map, undefined), + PeerPublicKey = maps:get(peer_public_key, Map, undefined), + ALPNProtocol = maps:get(alpn, Map, undefined), + update_start_state(State, Cipher, KeyShare, SessionId, + Group, SelectedSignAlg, PeerPublicKey, + ALPNProtocol). +%% update_start_state(#state{connection_states = ConnectionStates0, + handshake_env = #handshake_env{} = HsEnv, connection_env = CEnv, session = Session} = State, Cipher, KeyShare, SessionId, - Group, SelectedSignAlg, ClientPubKey) -> + Group, SelectedSignAlg, PeerPublicKey, ALPNProtocol) -> #{security_parameters := SecParamsR0} = PendingRead = maps:get(pending_read, ConnectionStates0), #{security_parameters := SecParamsW0} = PendingWrite = @@ -1003,11 +1434,12 @@ update_start_state(#state{connection_states = ConnectionStates0, ConnectionStates0#{pending_read => PendingRead#{security_parameters => SecParamsR}, pending_write => PendingWrite#{security_parameters => SecParamsW}}, State#state{connection_states = ConnectionStates, + handshake_env = HsEnv#handshake_env{alpn = ALPNProtocol}, key_share = KeyShare, session = Session#session{session_id = SessionId, ecc = Group, sign_alg = SelectedSignAlg, - dh_public_value = ClientPubKey, + dh_public_value = PeerPublicKey, cipher_suite = Cipher}, connection_env = CEnv#connection_env{negotiated_version = {3,4}}}. @@ -1071,25 +1503,41 @@ get_handshake_context_cv({[<<15,_/binary>>|Messages], _}) -> %% %% Drop all client messages from the front of the iolist using the property that %% incoming messages are binaries. -get_handshake_context({Messages, _}) -> - get_handshake_context(Messages); -get_handshake_context([H|T]) when is_binary(H) -> - get_handshake_context(T); -get_handshake_context(L) -> +get_handshake_context(server, {Messages, _}) -> + get_handshake_context_server(Messages); +get_handshake_context(client, {Messages, _}) -> + get_handshake_context_client(Messages). + +get_handshake_context_server([H|T]) when is_binary(H) -> + get_handshake_context_server(T); +get_handshake_context_server(L) -> + L. + + +get_handshake_context_client([H|T]) when is_list(H) -> + get_handshake_context_client(T); +get_handshake_context_client(L) -> L. +%% If the CertificateVerify message is sent by a server, the signature +%% algorithm MUST be one offered in the client's "signature_algorithms" +%% extension unless no valid certificate chain can be produced without +%% unsupported algorithms +%% %% If sent by a client, the signature algorithm used in the signature %% MUST be one of those present in the supported_signature_algorithms %% field of the "signature_algorithms" extension in the %% CertificateRequest message. -verify_signature_algorithm(#state{ssl_options = - #ssl_options{ - signature_algs = ServerSignAlgs}} = State0, - #certificate_verify_1_3{algorithm = ClientSignAlg}) -> - case lists:member(ClientSignAlg, ServerSignAlgs) of +verify_signature_algorithm(#state{ + static_env = #static_env{role = Role}, + ssl_options = + #ssl_options{ + signature_algs = LocalSignAlgs}} = State0, + #certificate_verify_1_3{algorithm = PeerSignAlg}) -> + case lists:member(PeerSignAlg, LocalSignAlgs) of true -> - ok; + {ok, maybe_update_selected_sign_alg(State0, PeerSignAlg, Role)}; false -> State1 = calculate_traffic_secrets(State0), State = ssl_record:step_encryption_state(State1), @@ -1098,11 +1546,19 @@ verify_signature_algorithm(#state{ssl_options = end. -verify_certificate_verify(#state{connection_states = ConnectionStates, - handshake_env = - #handshake_env{ - public_key_info = PublicKeyInfo, - tls_handshake_history = HHistory}} = State0, +maybe_update_selected_sign_alg(#state{session = Session} = State, SignAlg, client) -> + State#state{session = Session#session{sign_alg = SignAlg}}; +maybe_update_selected_sign_alg(State, _, _) -> + State. + + +verify_certificate_verify(#state{ + static_env = #static_env{role = Role}, + connection_states = ConnectionStates, + handshake_env = + #handshake_env{ + public_key_info = PublicKeyInfo, + tls_handshake_history = HHistory}} = State0, #certificate_verify_1_3{ algorithm = SignatureScheme, signature = Signature}) -> @@ -1120,12 +1576,11 @@ verify_certificate_verify(#state{connection_states = ConnectionStates, %% Transcript-Hash uses the HKDF hash function defined by the cipher suite. THash = tls_v1:transcript_hash(Context, HKDFAlgo), - PublicKey = get_public_key(PublicKeyInfo), + ContextString = peer_context_string(Role), %% Digital signatures use the hash function defined by the selected signature %% scheme. - case verify(THash, <<"TLS 1.3, client CertificateVerify">>, - HashAlgo, Signature, PublicKey) of + case verify(THash, ContextString, HashAlgo, Signature, PublicKeyInfo) of {ok, true} -> {ok, {State0, wait_finished}}; {ok, false} -> @@ -1139,6 +1594,19 @@ verify_certificate_verify(#state{connection_states = ConnectionStates, end. +context_string(server) -> + <<"TLS 1.3, server CertificateVerify">>; +context_string(client) -> + <<"TLS 1.3, client CertificateVerify">>. + + +%% Return context string for verifing peer signature +peer_context_string(server) -> + <<"TLS 1.3, client CertificateVerify">>; +peer_context_string(client) -> + <<"TLS 1.3, server CertificateVerify">>. + + %% If there is no overlap between the received %% "supported_groups" and the groups supported by the server, then the %% server MUST abort the handshake with a "handshake_failure" or an @@ -1172,14 +1640,36 @@ select_common_groups(ServerGroups, ClientGroups) -> %% for groups not listed in the client's "supported_groups" extension. %% Servers MAY check for violations of these rules and abort the %% handshake with an "illegal_parameter" alert if one is violated. -validate_key_share(_ ,[]) -> +validate_client_key_share(_ ,[]) -> ok; -validate_key_share([], _) -> +validate_client_key_share([], _) -> {error, illegal_parameter}; -validate_key_share([G|ClientGroups], [{_, G, _}|ClientShares]) -> - validate_key_share(ClientGroups, ClientShares); -validate_key_share([_|ClientGroups], [_|_] = ClientShares) -> - validate_key_share(ClientGroups, ClientShares). +validate_client_key_share([G|ClientGroups], [{_, G, _}|ClientShares]) -> + validate_client_key_share(ClientGroups, ClientShares); +validate_client_key_share([_|ClientGroups], [_|_] = ClientShares) -> + validate_client_key_share(ClientGroups, ClientShares). + + +%% Verify that selected group is offered by the client. +validate_server_key_share([G|_ClientGroups], {_, G, _}) -> + ok; +validate_server_key_share([_|ClientGroups], {_, _, _} = ServerKeyShare) -> + validate_server_key_share(ClientGroups, ServerKeyShare). + + +validate_selected_group(SelectedGroup, [SelectedGroup|_]) -> + {error, {illegal_parameter, + "Selected group sent by the server shall not correspond to a group" + " which was provided in the key_share extension"}}; +validate_selected_group(SelectedGroup, ClientGroups) -> + case lists:member(SelectedGroup, ClientGroups) of + true -> + ok; + false -> + {error, {illegal_parameter, + "Selected group sent by the server shall correspond to a group" + " which was provided in the supported_groups extension"}} + end. get_client_public_key([Group|_] = Groups, ClientShares) -> @@ -1197,32 +1687,79 @@ get_client_public_key([Group|Groups], ClientShares, PreferredGroup) -> get_client_public_key(Groups, ClientShares, PreferredGroup) end. +get_client_private_key([Group|_] = Groups, ClientShares) -> + get_client_private_key(Groups, ClientShares, Group). +%% +get_client_private_key(_, [], PreferredGroup) -> + {PreferredGroup, no_suitable_key}; +get_client_private_key([], _, PreferredGroup) -> + {PreferredGroup, no_suitable_key}; +get_client_private_key([Group|Groups], ClientShares, PreferredGroup) -> + case lists:keysearch(Group, 2, ClientShares) of + {value, {_, _, {_, ClientPrivateKey}}} -> + {Group, ClientPrivateKey}; + {value, {_, _, #'ECPrivateKey'{} = ClientPrivateKey}} -> + {Group, ClientPrivateKey}; + false -> + get_client_private_key(Groups, ClientShares, PreferredGroup) + end. + -%% get_client_public_key(Group, ClientShares) -> -%% case lists:keysearch(Group, 2, ClientShares) of -%% {value, {_, _, ClientPublicKey}} -> -%% ClientPublicKey; -%% false -> -%% %% 4.1.4. Hello Retry Request -%% %% -%% %% The server will send this message in response to a ClientHello -%% %% message if it is able to find an acceptable set of parameters but the -%% %% ClientHello does not contain sufficient information to proceed with -%% %% the handshake. -%% no_suitable_key -%% end. - -select_cipher_suite([], _) -> +get_server_public_key({key_share_entry, Group, PublicKey}) -> + {Group, PublicKey}. + + +%% RFC 7301 - Application-Layer Protocol Negotiation Extension +%% It is expected that a server will have a list of protocols that it +%% supports, in preference order, and will only select a protocol if the +%% client supports it. In that case, the server SHOULD select the most +%% highly preferred protocol that it supports and that is also +%% advertised by the client. In the event that the server supports no +%% protocols that the client advertises, then the server SHALL respond +%% with a fatal "no_application_protocol" alert. +handle_alpn(undefined, _) -> + {ok, undefined}; +handle_alpn([], _) -> + {error, no_application_protocol}; +handle_alpn([_|_], undefined) -> + {ok, undefined}; +handle_alpn([ServerProtocol|T], ClientProtocols) -> + case lists:member(ServerProtocol, ClientProtocols) of + true -> + {ok, ServerProtocol}; + false -> + handle_alpn(T, ClientProtocols) + end. + + +select_cipher_suite(_, [], _) -> {error, no_suitable_cipher}; -select_cipher_suite([Cipher|ClientCiphers], ServerCiphers) -> +%% If honor_cipher_order is set to true, use the server's preference for +%% cipher suite selection. +select_cipher_suite(true, ClientCiphers, ServerCiphers) -> + select_cipher_suite(false, ServerCiphers, ClientCiphers); +select_cipher_suite(false, [Cipher|ClientCiphers], ServerCiphers) -> case lists:member(Cipher, tls_v1:suites('TLS_v1.3')) andalso lists:member(Cipher, ServerCiphers) of true -> {ok, Cipher}; false -> - select_cipher_suite(ClientCiphers, ServerCiphers) + select_cipher_suite(false, ClientCiphers, ServerCiphers) end. + +%% RFC 8446 4.1.3 ServerHello +%% A client which receives a cipher suite that was not offered MUST abort the +%% handshake with an "illegal_parameter" alert. +validate_cipher_suite(Cipher, ClientCiphers) -> + case lists:member(Cipher, ClientCiphers) of + true -> + ok; + false -> + {error, illegal_parameter} + end. + + %% RFC 8446 (TLS 1.3) %% TLS 1.3 provides two extensions for indicating which signature %% algorithms may be used in digital signatures. The @@ -1246,15 +1783,20 @@ check_cert_sign_algo(SignAlgo, SignHash, _, ClientSignAlgsCert) -> %% DSA keys are not supported by TLS 1.3 select_sign_algo(dsa, _ClientSignAlgs, _ServerSignAlgs) -> {error, {insufficient_security, no_suitable_public_key}}; -%% TODO: Implement support for ECDSA keys! select_sign_algo(_, [], _) -> {error, {insufficient_security, no_suitable_signature_algorithm}}; select_sign_algo(PublicKeyAlgo, [C|ClientSignAlgs], ServerSignAlgs) -> {_, S, _} = ssl_cipher:scheme_to_components(C), %% RSASSA-PKCS1-v1_5 and Legacy algorithms are not defined for use in signed %% TLS handshake messages: filter sha-1 and rsa_pkcs1. + %% + %% RSASSA-PSS RSAE algorithms: If the public key is carried in an X.509 + %% certificate, it MUST use the rsaEncryption OID. + %% RSASSA-PSS PSS algorithms: If the public key is carried in an X.509 certificate, + %% it MUST use the RSASSA-PSS OID. case ((PublicKeyAlgo =:= rsa andalso S =:= rsa_pss_rsae) - orelse (PublicKeyAlgo =:= rsa_pss andalso S =:= rsa_pss_rsae)) + orelse (PublicKeyAlgo =:= rsa_pss andalso S =:= rsa_pss_pss) + orelse (PublicKeyAlgo =:= ecdsa andalso S =:= ecdsa)) andalso lists:member(C, ServerSignAlgs) of true -> @@ -1331,7 +1873,20 @@ get_supported_groups(#supported_groups{supported_groups = Groups}) -> Groups. get_key_shares(#key_share_client_hello{client_shares = ClientShares}) -> - ClientShares. + ClientShares; +get_key_shares(#key_share_server_hello{server_share = ServerShare}) -> + ServerShare. + +get_selected_group(#key_share_hello_retry_request{selected_group = SelectedGroup}) -> + SelectedGroup. + +get_alpn(ALPNProtocol0) -> + case ssl_handshake:decode_alpn(ALPNProtocol0) of + undefined -> + undefined; + [ALPNProtocol] -> + ALPNProtocol + end. maybe() -> Ref = erlang:make_ref(), diff --git a/lib/ssl/src/tls_handshake_1_3.hrl b/lib/ssl/src/tls_handshake_1_3.hrl index 7ae1b93e1c..eb85f216c8 100644 --- a/lib/ssl/src/tls_handshake_1_3.hrl +++ b/lib/ssl/src/tls_handshake_1_3.hrl @@ -74,29 +74,52 @@ y % opaque Y[coordinate_length]; }). +%% RFC 8446 4.2.9. Pre-Shared Key Exchange Modes + +%% enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode; -define(PSK_KE, 0). -define(PSK_DHE_KE, 1). --record(psk_keyexchange_modes, { +-record(psk_key_exchange_modes, { ke_modes % ke_modes<1..255> }). + +%% RFC 8446 4.2.10. Early Data Indication -record(empty, { }). -record(early_data_indication, { indication % uint32 max_early_data_size (new_session_ticket) | %% #empty{} (client_hello, encrypted_extensions) }). --record(psk_identity, { - identity, % opaque identity<1..2^16-1> - obfuscated_ticket_age % uint32 - }). --record(offered_psks, { - psk_identity, %identities<7..2^16-1>; - psk_binder_entry %binders<33..2^16-1>, opaque PskBinderEntry<32..255> - }). --record(pre_shared_keyextension,{ - extension %OfferedPsks (client_hello) | uint16 selected_identity (server_hello) - }). + +%% RFC 8446 4.2.11. Pre-Shared Key Extension +-record(psk_identity, + { + identity, % opaque identity<1..2^16-1> + obfuscated_ticket_age % uint32 + }). + +-record(offered_psks, + { + identities, % PskIdentity identities<7..2^16-1>; + binders % PskBinderEntry binders<33..2^16-1>; opaque PskBinderEntry<32..255> + }). + +%% struct { +%% select (Handshake.msg_type) { +%% case client_hello: OfferedPsks; +%% case server_hello: uint16 selected_identity; +%% }; +%% } PreSharedKeyExtension; +-record(pre_shared_key_client_hello, + { + offered_psks + }). + +-record(pre_shared_key_server_hello, + { + selected_identity + }). %% RFC 8446 B.3.1.2. -record(cookie, { diff --git a/lib/ssl/src/tls_record.erl b/lib/ssl/src/tls_record.erl index a5c550a429..2aeab98929 100644 --- a/lib/ssl/src/tls_record.erl +++ b/lib/ssl/src/tls_record.erl @@ -514,16 +514,27 @@ validate_tls_record_length(Versions, {_,Size0,_} = Q0, SslOpts, Acc, Type, Versi end. -binary_from_front(SplitSize, {Front,Size,Rear}) -> +binary_from_front(0, Q) -> + {<<>>, Q}; +binary_from_front(SplitSize, {Front,Size,Rear}) when SplitSize =< Size -> binary_from_front(SplitSize, Front, Size, Rear, []). %% -binary_from_front(SplitSize, [], Size, [_] = Rear, Acc) -> - %% Optimize a simple case - binary_from_front(SplitSize, Rear, Size, [], Acc); +%% SplitSize > 0 and there is at least SplitSize bytes buffered in Front and Rear binary_from_front(SplitSize, [], Size, Rear, Acc) -> - binary_from_front(SplitSize, lists:reverse(Rear), Size, [], Acc); + case Rear of + %% Avoid lists:reverse/1 for simple cases. + %% Case clause for [] to avoid infinite loop. + [_] -> + binary_from_front(SplitSize, Rear, Size, [], Acc); + [Bin2,Bin1] -> + binary_from_front(SplitSize, [Bin1,Bin2], Size, [], Acc); + [Bin3,Bin2,Bin1] -> + binary_from_front(SplitSize, [Bin1,Bin2,Bin3], Size, [], Acc); + [_,_,_|_] -> + binary_from_front(SplitSize, lists:reverse(Rear), Size, [], Acc) + end; binary_from_front(SplitSize, [Bin|Front], Size, Rear, []) -> - %% Optimize a frequent case + %% Optimize the frequent case when the accumulator is empty BinSize = byte_size(Bin), if SplitSize < BinSize -> diff --git a/lib/ssl/src/tls_record_1_3.erl b/lib/ssl/src/tls_record_1_3.erl index 74321a1ae2..d713062284 100644 --- a/lib/ssl/src/tls_record_1_3.erl +++ b/lib/ssl/src/tls_record_1_3.erl @@ -138,6 +138,15 @@ decode_cipher_text(#ssl_tls{type = ?ALERT, {#ssl_tls{type = ?ALERT, version = {3,4}, %% Internally use real version fragment = <<2,47>>}, ConnectionStates0}; +%% TLS 1.3 server can receive a User Cancelled Alert when handshake is +%% paused and then cancelled on the client side. +decode_cipher_text(#ssl_tls{type = ?ALERT, + version = ?LEGACY_VERSION, + fragment = <<2,90>>}, + ConnectionStates0) -> + {#ssl_tls{type = ?ALERT, + version = {3,4}, %% Internally use real version + fragment = <<2,90>>}, ConnectionStates0}; %% RFC8446 - TLS 1.3 %% D.4. Middlebox Compatibility Mode %% - If not offering early data, the client sends a dummy diff --git a/lib/ssl/test/Makefile b/lib/ssl/test/Makefile index dba90aaff0..0925c0facc 100644 --- a/lib/ssl/test/Makefile +++ b/lib/ssl/test/Makefile @@ -37,14 +37,29 @@ VSN=$(SSL_VSN) MODULES = \ ssl_test_lib \ + ssl_app_env_SUITE\ + ssl_alert_SUITE\ ssl_bench_test_lib \ ssl_dist_test_lib \ - ssl_alpn_handshake_SUITE \ + ssl_api_SUITE\ + tls_api_SUITE\ ssl_basic_SUITE \ ssl_bench_SUITE \ ssl_cipher_SUITE \ ssl_cipher_suite_SUITE \ - openssl_server_cipher_suite_SUITE\ + openssl_cipher_suite_SUITE\ + ssl_alpn_SUITE \ + openssl_alpn_SUITE\ + ssl_npn_SUITE \ + openssl_npn_SUITE\ + openssl_sni_SUITE\ + ssl_renegotiate_SUITE\ + openssl_renegotiate_SUITE\ + openssl_reject_SUITE\ + ssl_cert_tests\ + ssl_cert_SUITE\ + openssl_server_cert_SUITE\ + openssl_client_cert_SUITE\ ssl_certificate_verify_SUITE\ ssl_crl_SUITE\ ssl_dist_SUITE \ @@ -52,12 +67,12 @@ MODULES = \ ssl_engine_SUITE\ ssl_handshake_SUITE \ ssl_npn_hello_SUITE \ - ssl_npn_handshake_SUITE \ ssl_packet_SUITE \ ssl_payload_SUITE \ ssl_pem_cache_SUITE \ + ssl_session_SUITE \ ssl_session_cache_SUITE \ - ssl_to_openssl_SUITE \ + openssl_session_SUITE \ ssl_ECC_SUITE \ ssl_ECC_openssl_SUITE \ ssl_ECC\ @@ -65,6 +80,10 @@ MODULES = \ ssl_sni_SUITE \ ssl_eqc_SUITE \ ssl_rfc_5869_SUITE \ + tls_1_3_record_SUITE\ + openssl_tls_1_3_version_SUITE\ + tls_1_3_version_SUITE\ + ssl_socket_SUITE\ make_certs \ x509_test \ inet_crypto_dist diff --git a/lib/ssl/test/openssl_alpn_SUITE.erl b/lib/ssl/test/openssl_alpn_SUITE.erl new file mode 100644 index 0000000000..5008dba922 --- /dev/null +++ b/lib/ssl/test/openssl_alpn_SUITE.erl @@ -0,0 +1,421 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(openssl_alpn_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +-define(OPENSSL_QUIT, "Q\n"). +-define(OPENSSL_RENEGOTIATE, "R\n"). +-define(SLEEP, 1000). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +all() -> + %% Note: ALPN not supported in sslv3 + case ssl_test_lib:openssl_sane_dtls_alpn() of + true -> + [ + {group, 'tlsv1.3'}, + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'dtlsv1.2'}, + {group, 'dtlsv1'}]; + false -> + [{group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}] + end. + +groups() -> + case ssl_test_lib:openssl_sane_dtls_alpn() of + true -> + [ + {'tlsv1.3', [], alpn_tests()}, + {'tlsv1.2', [], alpn_tests() ++ alpn_npn_coexist() ++ rengotiation_tests()}, + {'tlsv1.1', [], alpn_tests() ++ alpn_npn_coexist() ++ rengotiation_tests()}, + {'tlsv1', [], alpn_tests() ++ alpn_npn_coexist() ++ rengotiation_tests()}, + {'dtlsv1.2', [], alpn_tests()}, + {'dtlsv1', [], alpn_tests()} + ]; + false -> + [ + {'tlsv1.3', [], alpn_tests()}, + {'tlsv1.2', [], alpn_tests() ++ alpn_npn_coexist() ++ rengotiation_tests()}, + {'tlsv1.1', [], alpn_tests() ++ alpn_npn_coexist() ++ rengotiation_tests()}, + {'tlsv1', [], alpn_tests() ++ alpn_npn_coexist() ++ rengotiation_tests()} + ] + end. + +alpn_tests() -> + [erlang_client_alpn_openssl_server_alpn, + erlang_server_alpn_openssl_client_alpn, + erlang_client_alpn_openssl_server, + erlang_client_openssl_server_alpn, + erlang_server_alpn_openssl_client, + erlang_server_openssl_client_alpn]. + +alpn_npn_coexist() -> + [ + erlang_client_alpn_npn_openssl_server_alpn_npn, + erlang_server_alpn_npn_openssl_client_alpn_npn + ]. +rengotiation_tests() -> + case ssl_test_lib:sane_openssl_alpn_npn_renegotiate() of + true -> + [erlang_client_alpn_openssl_server_alpn_renegotiate, + erlang_server_alpn_openssl_client_alpn_renegotiate]; + false -> + [] + end. +init_per_suite(Config0) -> + case os:find_executable("openssl") of + false -> + {skip, "Openssl not found"}; + _ -> + case check_openssl_alpn_support(Config0) of + {skip, _} = Skip -> + Skip; + _ -> + ct:pal("Version: ~p", [os:cmd("openssl version")]), + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + ssl_test_lib:make_rsa_cert(Config0) + catch _:_ -> + {skip, "Crypto did not start"} + end + end + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto), + ssl_test_lib:kill_openssl(). + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:supports_ssl_tls_version(GroupName) of + true -> + case ssl_test_lib:check_sane_openssl_version(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName, Config); + false -> + {skip, openssl_does_not_support_version} + end; + false -> + {skip, openssl_does_not_support_version} + end; + _ -> + Config + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +init_per_testcase(TestCase, Config) -> + ct:timetrap({seconds, 10}), + special_init(TestCase, Config). + +special_init(TestCase, Config) + when TestCase == erlang_client_alpn_openssl_server_alpn_renegotiate; + TestCase == erlang_server_alpn_openssl_client_alpn_renegotiate -> + {ok, Version} = application:get_env(ssl, protocol_version), + ssl_test_lib:check_sane_openssl_renegotaite(Config, Version); +special_init(TestCase, Config) + when TestCase == erlang_client_alpn_npn_openssl_server_alpn_npn; + TestCase == erlang_server_alpn_npn_openssl_client_alpn_npn -> + ssl_test_lib:check_openssl_npn_support(Config); +special_init(_, Config) -> + Config. + +end_per_testcase(_, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +erlang_client_alpn_openssl_server_alpn(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Client, Data) + end). + +%%-------------------------------------------------------------------- + +erlang_server_alpn_openssl_client_alpn(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Client, Data) + end). + +%%-------------------------------------------------------------------------- + +erlang_client_alpn_openssl_server(Config) when is_list(Config) -> + Data = "From openssl to erlang", + ssl_test_lib:start_erlang_client_and_openssl_server_with_opts(Config, + [{alpn_advertised_protocols, [<<"spdy/2">>]}], + [], + Data, fun(Client, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Client, Data) + end). + +%%-------------------------------------------------------------------------- + +erlang_client_openssl_server_alpn(Config) when is_list(Config) -> + Data = "From openssl to erlang", + ssl_test_lib:start_erlang_client_and_openssl_server_with_opts(Config, + [], + ["-alpn", "spdy/2"], + Data, fun(Client, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Client, Data) + end). + +%%-------------------------------------------------------------------------- + +erlang_server_alpn_openssl_client(Config) when is_list(Config) -> + Data = "From openssl to erlang", + ssl_test_lib:start_erlang_server_and_openssl_client_with_opts(Config, + [{alpn_preferred_protocols, [<<"spdy/2">>]}], + [], + Data, fun(Server, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, Data) + end). + +%%-------------------------------------------------------------------------- + +erlang_server_openssl_client_alpn(Config) when is_list(Config) -> + Data = "From openssl to erlang", + ssl_test_lib:start_erlang_server_and_openssl_client_with_opts(Config, + [], + ["-alpn", "spdy/2"], + Data, fun(Server, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, Data) + end). + +%%-------------------------------------------------------------------- + +erlang_client_alpn_openssl_server_alpn_renegotiate(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) -> + true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), + ct:sleep(?SLEEP), + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Client, Data) + end). + +%%-------------------------------------------------------------------- + +erlang_server_alpn_openssl_client_alpn_renegotiate(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, fun(Server, OpensslPort) -> + true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), + ct:sleep(?SLEEP), + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, Data) + end). + +%%-------------------------------------------------------------------- + +erlang_client_alpn_npn_openssl_server_alpn_npn(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_client_and_openssl_server_for_alpn_npn_negotiation(Config, Data, fun(Client, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Client, Data) + end). + +%%-------------------------------------------------------------------- + +erlang_server_alpn_npn_openssl_client_alpn_npn(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_server_and_openssl_client_for_alpn_npn_negotiation(Config, Data, fun(Server, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, Data) + end). + + +%%-------------------------------------------------------------------- +%% Internal functions ----------------------------------------------- +%%-------------------------------------------------------------------- +check_openssl_alpn_support(Config) -> + HelpText = os:cmd("openssl s_client --help"), + case string:str(HelpText, "alpn") of + 0 -> + {skip, "Openssl not compiled with alpn support"}; + _ -> + Config + end. + +start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, Callback) -> + process_flag(trap_exit, true), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ClientOpts = [{alpn_advertised_protocols, [<<"spdy/2">>]} | ClientOpts0], + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Version = ssl_test_lib:protocol_version(Config), + + Exe = "openssl", + Args = ["s_server", "-msg", "-alpn", "http/1.1,spdy/2", "-accept", + integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, + "-cert", CertFile, "-key", KeyFile], + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}}, + {options, [{reuse_sessions, false} | ClientOpts]}]), + + Callback(Client, OpensslPort), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + + ssl_test_lib:close(Client), + process_flag(trap_exit, false). + +start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, Callback) -> + process_flag(trap_exit, true), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>]} | ServerOpts0], + + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Version = ssl_test_lib:protocol_version(Config), + + Exe = "openssl", + Args = ["s_client", "-alpn", "http/1.0,spdy/2", "-msg", "-port", + integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-host", "localhost"], + + OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), + + Callback(Server, OpenSslPort), + + ssl_test_lib:close(Server), + + ssl_test_lib:close_port(OpenSslPort), + process_flag(trap_exit, false). + +start_erlang_client_and_openssl_server_for_alpn_npn_negotiation(Config, Data, Callback) -> + process_flag(trap_exit, true), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ClientOpts = [{alpn_advertised_protocols, [<<"spdy/2">>]}, + {client_preferred_next_protocols, {client, [<<"spdy/3">>, <<"http/1.1">>]}} | ClientOpts0], + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Version = ssl_test_lib:protocol_version(Config), + + Exe = "openssl", + Args = ["s_server", "-msg", "-alpn", "http/1.1,spdy/2", "-nextprotoneg", + "spdy/3", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-cert", CertFile, "-key", KeyFile], + + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}}, + {options, [{reuse_sessions, false} | ClientOpts]}]), + + Callback(Client, OpensslPort), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + + ssl_test_lib:close(Client), + process_flag(trap_exit, false). + +start_erlang_server_and_openssl_client_for_alpn_npn_negotiation(Config, Data, Callback) -> + process_flag(trap_exit, true), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>]}, + {next_protocols_advertised, [<<"spdy/3">>, <<"http/1.1">>]} | ServerOpts0], + + {_, ServerNode, _} = ssl_test_lib:run_where(Config), + + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Version = ssl_test_lib:protocol_version(Config), + Exe = "openssl", + Args = ["s_client", "-alpn", "http/1.1,spdy/2", "-nextprotoneg", "spdy/3", + "-msg", "-port", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-host", "localhost"], + OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), + + Callback(Server, OpenSslPort), + + ssl_test_lib:close(Server), + ssl_test_lib:close_port(OpenSslPort), + process_flag(trap_exit, false). + diff --git a/lib/ssl/test/openssl_server_cipher_suite_SUITE.erl b/lib/ssl/test/openssl_cipher_suite_SUITE.erl index 6ce34ce7fa..955eb914c0 100644 --- a/lib/ssl/test/openssl_server_cipher_suite_SUITE.erl +++ b/lib/ssl/test/openssl_cipher_suite_SUITE.erl @@ -20,7 +20,7 @@ %% --module(openssl_server_cipher_suite_SUITE). +-module(openssl_cipher_suite_SUITE). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -31,19 +31,26 @@ %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- all() -> - [ - {group, 'tlsv1.2'}, + [ + {group, openssl_server}, + {group, openssl_client} + ]. + +all_protocol_groups() -> + [{group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, {group, 'sslv3'}, {group, 'dtlsv1.2'}, {group, 'dtlsv1'} - ]. + ]. groups() -> %% TODO: Enable SRP, PSK suites (needs OpenSSL s_server conf) %% TODO: Enable all "kex" on DTLS [ + {openssl_server, all_protocol_groups()}, + {openssl_client, all_protocol_groups()}, {'tlsv1.2', [], kex()}, {'tlsv1.1', [], kex()}, {'tlsv1', [], kex()}, @@ -135,6 +142,9 @@ kex() -> dtls_kex() -> %% Should be all kex in the future dtls_rsa() ++ dss() ++ anonymous(). +ssl3_kex() -> + ssl3_rsa() ++ ssl3_dss() ++ ssl3_anonymous(). + rsa() -> [{group, dhe_rsa}, {group, ecdhe_rsa}, @@ -148,6 +158,11 @@ dtls_rsa() -> %%,{group, rsa_psk} ]. +ssl3_rsa() -> + [{group, dhe_rsa}, + {group, rsa} + ]. + ecdsa() -> [{group, ecdhe_ecdsa}]. @@ -156,6 +171,10 @@ dss() -> %%{group, srp_dss} ]. +ssl3_dss() -> + [{group, dhe_dss} + ]. + anonymous() -> [{group, dh_anon}, {group, ecdh_anon} @@ -165,6 +184,9 @@ anonymous() -> %%{group, srp_anon} ]. +ssl3_anonymous() -> + [{group, dh_anon}]. + init_per_suite(Config) -> catch crypto:stop(), try crypto:start() of @@ -177,7 +199,8 @@ init_per_suite(Config) -> end_per_suite(_Config) -> ssl:stop(), - application:stop(crypto). + application:stop(crypto), + ssl_test_lib:kill_openssl(). %%-------------------------------------------------------------------- init_per_group(GroupName, Config) -> @@ -198,7 +221,12 @@ init_per_group(GroupName, Config) -> false -> do_init_per_group(GroupName, Config) end. - +do_init_per_group(openssl_client, Config0) -> + Config = proplists:delete(server_type, proplists:delete(client_type, Config0)), + [{client_type, openssl}, {server_type, erlang} | Config]; +do_init_per_group(openssl_server, Config0) -> + Config = proplists:delete(server_type, proplists:delete(client_type, Config0)), + [{client_type, erlang}, {server_type, openssl} | Config]; do_init_per_group(GroupName, Config) when GroupName == ecdh_anon; GroupName == ecdhe_rsa; GroupName == ecdhe_psk -> @@ -746,8 +774,7 @@ cipher_suite_test(CipherSuite, _Version, Config) -> ct:log("Testing CipherSuite ~p~n", [CipherSuite]), ct:log("Server Opts ~p~n", [ServerOpts]), ct:log("Client Opts ~p~n", [ClientOpts]), - ssl_test_lib:basic_test([{ciphers, [CipherSuite]} | COpts], SOpts, [{client_type, erlang}, - {server_type, openssl} | Config]). + ssl_test_lib:basic_test([{ciphers, [CipherSuite]} | COpts], SOpts, Config). test_ciphers(Kex, Cipher, Version) -> diff --git a/lib/ssl/test/openssl_client_cert_SUITE.erl b/lib/ssl/test/openssl_client_cert_SUITE.erl new file mode 100644 index 0000000000..b327988744 --- /dev/null +++ b/lib/ssl/test/openssl_client_cert_SUITE.erl @@ -0,0 +1,350 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(openssl_client_cert_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). +-include_lib("common_test/include/ct.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +all() -> + [ + {group, openssl_client} + ]. + +groups() -> + [ + {openssl_client, [], protocol_groups()}, + {'tlsv1.3', [], tls_1_3_protocol_groups()}, + {'tlsv1.2', [], pre_tls_1_3_protocol_groups()}, + {'tlsv1.1', [], pre_tls_1_3_protocol_groups()}, + {'tlsv1', [], pre_tls_1_3_protocol_groups()}, + {'sslv3', [], ssl_protocol_groups()}, + {'dtlsv1.2', [], pre_tls_1_3_protocol_groups()}, + {'dtlsv1', [], pre_tls_1_3_protocol_groups()}, + {rsa, [], all_version_tests()}, + {ecdsa, [], all_version_tests()}, + {dsa, [], all_version_tests()}, + {rsa_1_3, [], all_version_tests() ++ tls_1_3_tests() ++ [unsupported_sign_algo_client_auth, + unsupported_sign_algo_cert_client_auth]}, + {ecdsa_1_3, [], all_version_tests() ++ tls_1_3_tests()} + ]. + +protocol_groups() -> + [{group, 'tlsv1.3'}, + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}, + {group, 'dtlsv1.2'}, + {group, 'dtlsv1'} + ]. + +ssl_protocol_groups() -> + [{group, rsa}, + {group, dsa}]. + +pre_tls_1_3_protocol_groups() -> + [{group, rsa}, + {group, ecdsa}, + {group, dsa}]. + +tls_1_3_protocol_groups() -> + [{group, rsa_1_3}, + {group, ecdsa_1_3}]. + +tls_1_3_tests() -> + [ + hello_retry_request, + custom_groups, + hello_retry_client_auth, + hello_retry_client_auth_empty_cert_accepted, + hello_retry_client_auth_empty_cert_rejected + ]. + +all_version_tests() -> + [ + no_auth, + auth, + client_auth_empty_cert_accepted, + client_auth_empty_cert_rejected, + client_auth_partial_chain, + client_auth_allow_partial_chain, + client_auth_do_not_allow_partial_chain, + client_auth_partial_chain_fun_fail, + missing_root_cert_no_auth + %%invalid_signature_client + ]. + +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + Config + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:unload(ssl), + application:stop(crypto). + +init_per_group(openssl_client, Config0) -> + Config = proplists:delete(server_type, proplists:delete(client_type, Config0)), + [{client_type, openssl}, {server_type, erlang} | Config]; +init_per_group(Group, Config0) when Group == rsa; + Group == rsa_1_3 -> + Config = ssl_test_lib:make_rsa_cert(Config0), + COpts = proplists:get_value(client_rsa_opts, Config), + SOpts = proplists:get_value(server_rsa_opts, Config), + %% Make sure _rsa* suite is choosen by ssl_test_lib:start_server + Version = proplists:get_value(version,Config), + Ciphers = ssl_cert_tests:test_ciphers(fun(dhe_rsa) -> + true; + (ecdhe_rsa) -> + true; + (_) -> + false + end, Version), + case Ciphers of + [_|_] -> + [{cert_key_alg, rsa} | + lists:delete(cert_key_alg, + [{client_cert_opts, [{ciphers, Ciphers} | COpts]}, + {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, + lists:delete(client_cert_opts, Config))])]; + [] -> + {skip, {no_sup, Group, Version}} + end; +init_per_group(Group, Config0) when Group == ecdsa; + Group == ecdsa_1_3 -> + PKAlg = crypto:supports(public_keys), + case lists:member(ecdsa, PKAlg) andalso (lists:member(ecdh, PKAlg) orelse + lists:member(dh, PKAlg)) of + true -> + Config = ssl_test_lib:make_ecdsa_cert(Config0), + COpts = proplists:get_value(client_ecdsa_opts, Config), + SOpts = proplists:get_value(server_ecdsa_opts, Config), + %% Make sure ecdh* suite is choosen by ssl_test_lib:start_server + Version = proplists:get_value(version,Config), + Ciphers = ssl_cert_tests:test_ciphers(fun(ecdh_ecdsa) -> + true; + (ecdhe_ecdsa) -> + true; + (_) -> + false + end, Version), + case Ciphers of + [_|_] -> + [{cert_key_alg, ecdsa} | + lists:delete(cert_key_alg, + [{client_cert_opts, [{ciphers, Ciphers} | COpts]}, + {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, + lists:delete(client_cert_opts, Config))] + )]; + [] -> + {skip, {no_sup, Group, Version}} + end; + false -> + {skip, "Missing EC crypto support"} + end; +init_per_group(Group, Config0) when Group == dsa -> + PKAlg = crypto:supports(public_keys), + case lists:member(dss, PKAlg) andalso lists:member(dh, PKAlg) of + true -> + Config = ssl_test_lib:make_dsa_cert(Config0), + COpts = proplists:get_value(client_dsa_opts, Config), + SOpts = proplists:get_value(server_dsa_opts, Config), + %% Make sure dhe_dss* suite is choosen by ssl_test_lib:start_server + Version = proplists:get_value(version,Config), + Ciphers = ssl_cert_tests:test_ciphers(fun(dh_dss) -> + true; + (dhe_dss) -> + true; + (_) -> + false + end, Version), + case Ciphers of + [_|_] -> + [{cert_key_alg, dsa} | + lists:delete(cert_key_alg, + [{client_cert_opts, [{ciphers, Ciphers} | COpts]}, + {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, + lists:delete(client_cert_opts, Config))])]; + [] -> + {skip, {no_sup, Group, Version}} + end; + false -> + {skip, "Missing DSS crypto support"} + end; +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:check_sane_openssl_version(GroupName) of + true -> + [{version, GroupName} + | ssl_test_lib:init_tls_version(GroupName, Config)]; + false -> + {skip, "Missing openssl support"} + end; + _ -> + ssl:start(), + Config + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. +init_per_testcase(TestCase, Config) when + TestCase == client_auth_empty_cert_accepted; + TestCase == client_auth_empty_cert_rejected -> + Version = proplists:get_value(version,Config), + case Version of + sslv3 -> + %% Openssl client sends "No Certificate Reserved" warning ALERT + %% instead of sending EMPTY cert message in SSL-3.0 so empty cert test are not + %% relevant + {skip, openssl_behaves_differently}; + _ -> + ssl_test_lib:ct_log_supported_protocol_versions(Config), + ct:timetrap({seconds, 10}), + Config + end; +init_per_testcase(_TestCase, Config) -> + ssl_test_lib:ct_log_supported_protocol_versions(Config), + ct:timetrap({seconds, 10}), + Config. + +end_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +no_auth() -> + ssl_cert_tests:no_auth(). + +no_auth(Config) -> + ssl_cert_tests:no_auth(Config). +%%-------------------------------------------------------------------- +auth() -> + ssl_cert_tests:auth(). +auth(Config) -> + ssl_cert_tests:auth(Config). +%%-------------------------------------------------------------------- +client_auth_empty_cert_accepted() -> + ssl_cert_tests:client_auth_empty_cert_accepted(). +client_auth_empty_cert_accepted(Config) -> + ssl_cert_tests:client_auth_empty_cert_accepted(Config). +%%-------------------------------------------------------------------- +client_auth_empty_cert_rejected() -> + ssl_cert_tests:client_auth_empty_cert_rejected(). +client_auth_empty_cert_rejected(Config) -> + ssl_cert_tests:client_auth_empty_cert_rejected(Config). +%%-------------------------------------------------------------------- +client_auth_partial_chain() -> + ssl_cert_tests:client_auth_partial_chain(). +client_auth_partial_chain(Config) when is_list(Config) -> + ssl_cert_tests:client_auth_partial_chain(Config). + +%%-------------------------------------------------------------------- +client_auth_allow_partial_chain() -> + ssl_cert_tests:client_auth_allow_partial_chain(). +client_auth_allow_partial_chain(Config) when is_list(Config) -> + ssl_cert_tests:client_auth_allow_partial_chain(Config). +%%-------------------------------------------------------------------- +client_auth_do_not_allow_partial_chain() -> + ssl_cert_tests:client_auth_do_not_allow_partial_chain(). +client_auth_do_not_allow_partial_chain(Config) when is_list(Config) -> + ssl_cert_tests:client_auth_do_not_allow_partial_chain(Config). + +%%-------------------------------------------------------------------- +client_auth_partial_chain_fun_fail() -> + ssl_cert_tests:client_auth_partial_chain_fun_fail(). +client_auth_partial_chain_fun_fail(Config) when is_list(Config) -> + ssl_cert_tests:client_auth_partial_chain_fun_fail(Config). + +%%-------------------------------------------------------------------- +missing_root_cert_no_auth() -> + ssl_cert_tests:missing_root_cert_no_auth(). +missing_root_cert_no_auth(Config) when is_list(Config) -> + ssl_cert_tests:missing_root_cert_no_auth(Config). + +%%-------------------------------------------------------------------- +invalid_signature_client() -> + ssl_cert_tests:invalid_signature_client(). +invalid_signature_client(Config) when is_list(Config) -> + ssl_cert_tests:invalid_signature_client(Config). +%%-------------------------------------------------------------------- +invalid_signature_server() -> + ssl_cert_tests:invalid_signature_client(). +invalid_signature_server(Config) when is_list(Config) -> + ssl_cert_tests:invalid_signature_client(Config). + +%%-------------------------------------------------------------------- +%% TLS 1.3 Test Cases ------------------------------------------------ +%%-------------------------------------------------------------------- +hello_retry_request() -> + ssl_cert_tests:hello_retry_request(). +hello_retry_request(Config) -> + ssl_cert_tests:hello_retry_request(Config). +%%-------------------------------------------------------------------- +custom_groups() -> + ssl_cert_tests:custom_groups(). +custom_groups(Config) -> + ssl_cert_tests:custom_groups(Config). +unsupported_sign_algo_cert_client_auth() -> + ssl_cert_tests:unsupported_sign_algo_cert_client_auth(). +unsupported_sign_algo_cert_client_auth(Config) -> + ssl_cert_tests:unsupported_sign_algo_cert_client_auth(Config). +unsupported_sign_algo_client_auth() -> + ssl_cert_tests:unsupported_sign_algo_client_auth(). +unsupported_sign_algo_client_auth(Config) -> + ssl_cert_tests:unsupported_sign_algo_client_auth(Config). +%%-------------------------------------------------------------------- +hello_retry_client_auth() -> + ssl_cert_tests:hello_retry_client_auth(). +hello_retry_client_auth(Config) -> + ssl_cert_tests:hello_retry_client_auth(Config). +%%-------------------------------------------------------------------- +hello_retry_client_auth_empty_cert_accepted() -> + ssl_cert_tests:hello_retry_client_auth_empty_cert_accepted(). +hello_retry_client_auth_empty_cert_accepted(Config) -> + ssl_cert_tests:hello_retry_client_auth_empty_cert_accepted(Config). +%%-------------------------------------------------------------------- +hello_retry_client_auth_empty_cert_rejected() -> + ssl_cert_tests:hello_retry_client_auth_empty_cert_rejected(). +hello_retry_client_auth_empty_cert_rejected(Config) -> + ssl_cert_tests:hello_retry_client_auth_empty_cert_rejected(Config). diff --git a/lib/ssl/test/openssl_npn_SUITE.erl b/lib/ssl/test/openssl_npn_SUITE.erl new file mode 100644 index 0000000000..0294f4997f --- /dev/null +++ b/lib/ssl/test/openssl_npn_SUITE.erl @@ -0,0 +1,311 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(openssl_npn_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +-define(OPENSSL_QUIT, "Q\n"). +-define(OPENSSL_RENEGOTIATE, "R\n"). +-define(SLEEP, 1000). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +all() -> + %% NPN is not supported in TLS-1.3 (replaced by ALPN and deprecated in TLS 1.2) + %% OpenSSL DTLS support for NPN is either not there or broken. + [{group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}]. + +groups() -> + [{'tlsv1.2', [], npn_tests() ++ npn_renegotiate_tests()}, + {'tlsv1.1', [], npn_tests() ++ npn_renegotiate_tests()}, + {'tlsv1', [], npn_tests() ++ npn_renegotiate_tests()} + ]. + +npn_tests() -> + [erlang_client_openssl_server_npn, + erlang_server_openssl_client_npn, + erlang_server_openssl_client_npn_only_client, + erlang_server_openssl_client_npn_only_server, + erlang_client_openssl_server_npn_only_client, + erlang_client_openssl_server_npn_only_server]. + +npn_renegotiate_tests() -> + case ssl_test_lib:sane_openssl_alpn_npn_renegotiate() of + true -> + [erlang_server_openssl_client_npn_renegotiate, + erlang_client_openssl_server_npn_renegotiate]; + false -> + [] + end. + +init_per_suite(Config0) -> + case os:find_executable("openssl") of + false -> + {skip, "Openssl not found"}; + _ -> + case check_openssl_npn_support(Config0) of + {skip, _} = Skip -> + Skip; + _ -> + ct:pal("Version: ~p", [os:cmd("openssl version")]), + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + ssl_test_lib:make_rsa_cert(Config0) + catch _:_ -> + {skip, "Crypto did not start"} + end + end + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto), + ssl_test_lib:kill_openssl(). + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:supports_ssl_tls_version(GroupName) of + true -> + case ssl_test_lib:check_sane_openssl_version(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName, Config); + false -> + {skip, openssl_does_not_support_version} + end; + false -> + {skip, openssl_does_not_support_version} + end; + _ -> + Config + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +init_per_testcase(TestCase, Config) -> + ct:timetrap({seconds, 10}), + special_init(TestCase, Config). + +special_init(TestCase, Config) + when TestCase == erlang_client_npn_openssl_server_npn_renegotiate; + TestCase == erlang_server_npn_openssl_client_npn_renegotiate -> + {ok, Version} = application:get_env(ssl, protocol_version), + ssl_test_lib:check_sane_openssl_renegotaite(Config, Version); +special_init(_, Config) -> + Config. + +end_per_testcase(_, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +erlang_client_openssl_server_npn() -> + [{doc,"Test erlang client with openssl server doing npn negotiation"}]. + +erlang_client_openssl_server_npn(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, + fun(Client, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Client, Data) + end). + +%%-------------------------------------------------------------------- +erlang_client_openssl_server_npn_renegotiate() -> + [{doc,"Test erlang client with openssl server doing npn negotiation and renegotiate"}]. + +erlang_client_openssl_server_npn_renegotiate(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, + fun(Client, OpensslPort) -> + true = port_command(OpensslPort, + ?OPENSSL_RENEGOTIATE), + ct:sleep(?SLEEP), + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Client, Data) + end). +%%-------------------------------------------------------------------------- +erlang_server_openssl_client_npn() -> + [{doc,"Test erlang server with openssl client and npn negotiation"}]. + +erlang_server_openssl_client_npn(Config) when is_list(Config) -> + + Data = "From openssl to erlang", + start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, + fun(Server, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, Data) + end). + +%%-------------------------------------------------------------------------- +erlang_server_openssl_client_npn_renegotiate() -> + [{doc,"Test erlang server with openssl client and npn negotiation with renegotiation"}]. + +erlang_server_openssl_client_npn_renegotiate(Config) when is_list(Config) -> + Data = "From openssl to erlang", + start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, + fun(Server, OpensslPort) -> + true = port_command(OpensslPort, + ?OPENSSL_RENEGOTIATE), + ct:sleep(?SLEEP), + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, Data) + end). +%%-------------------------------------------------------------------------- +erlang_client_openssl_server_npn_only_server(Config) when is_list(Config) -> + Data = "From openssl to erlang", + ssl_test_lib:start_erlang_client_and_openssl_server_with_opts(Config, [], + ["-nextprotoneg", "spdy/2"], Data, + fun(Server, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, Data) + end). + +%%-------------------------------------------------------------------------- + +erlang_client_openssl_server_npn_only_client(Config) when is_list(Config) -> + Data = "From openssl to erlang", + ssl_test_lib:start_erlang_client_and_openssl_server_with_opts(Config, + [{client_preferred_next_protocols, + {client, [<<"spdy/2">>], <<"http/1.1">>}}], [], + Data, + fun(Server, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, Data) + end). + +%%-------------------------------------------------------------------------- +erlang_server_openssl_client_npn_only_server(Config) when is_list(Config) -> + Data = "From openssl to erlang", + ssl_test_lib:start_erlang_server_and_openssl_client_with_opts(Config, + [{next_protocols_advertised, [<<"spdy/2">>]}], [], + Data, + fun(Server, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, Data) + end). + +erlang_server_openssl_client_npn_only_client(Config) when is_list(Config) -> + Data = "From openssl to erlang", + ssl_test_lib:start_erlang_server_and_openssl_client_with_opts(Config, [], ["-nextprotoneg", "spdy/2"], + Data, + fun(Server, OpensslPort) -> + true = port_command(OpensslPort, Data), + ssl_test_lib:check_result(Server, Data) + end). + +%%-------------------------------------------------------------------- +%% Internal functions ----------------------------------------------- +%%-------------------------------------------------------------------- + +start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callback) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ClientOpts = [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}} | ClientOpts0], + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Version = ssl_test_lib:protocol_version(Config), + + Exe = "openssl", + Args = ["s_server", "-msg", "-nextprotoneg", "http/1.1,spdy/2", "-accept", integer_to_list(Port), + ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, + "-cert", CertFile, "-key", KeyFile], + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}}, + {options, ClientOpts}]), + + Callback(Client, OpensslPort), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + + ssl_test_lib:close(Client), + process_flag(trap_exit, false). + +start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callback) -> + process_flag(trap_exit, true), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>]} | ServerOpts0], + + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Version = ssl_test_lib:protocol_version(Config), + + Exe = "openssl", + Args = ["s_client", "-nextprotoneg", "http/1.0,spdy/2", "-msg", "-connect", + ssl_test_lib:hostname_format(Hostname) ++ ":" + ++ integer_to_list(Port), ssl_test_lib:version_flag(Version)], + + OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), + + Callback(Server, OpenSslPort), + + ssl_test_lib:close(Server), + + ssl_test_lib:close_port(OpenSslPort), + process_flag(trap_exit, false). + +check_openssl_npn_support(Config) -> + HelpText = os:cmd("openssl s_client --help"), + case string:str(HelpText, "nextprotoneg") of + 0 -> + {skip, "Openssl not compiled with nextprotoneg support"}; + _ -> + Config + end. diff --git a/lib/ssl/test/openssl_reject_SUITE.erl b/lib/ssl/test/openssl_reject_SUITE.erl new file mode 100644 index 0000000000..deefd11823 --- /dev/null +++ b/lib/ssl/test/openssl_reject_SUITE.erl @@ -0,0 +1,209 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(openssl_reject_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +-define(SLEEP, 1000). +-define(OPENSSL_GARBAGE, "P\n"). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +all() -> + [{group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}]. + +groups() -> + [{'tlsv1.2', [], all_versions_tests()}, + {'tlsv1.1', [], all_versions_tests()}, + {'tlsv1', [], all_versions_tests()}, + {'sslv3', [], all_versions_tests()} + ]. + +all_versions_tests() -> + [ + erlang_client_bad_openssl_server, + ssl2_erlang_server_openssl_client + ]. + +init_per_suite(Config0) -> + case os:find_executable("openssl") of + false -> + {skip, "Openssl not found"}; + _ -> + ct:pal("Version: ~p", [os:cmd("openssl version")]), + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + ssl_test_lib:make_rsa_cert(Config0) + catch _:_ -> + {skip, "Crypto did not start"} + end + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto), + ssl_test_lib:kill_openssl(). + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:supports_ssl_tls_version(GroupName) of + true -> + case ssl_test_lib:check_sane_openssl_version(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName, Config); + false -> + {skip, openssl_does_not_support_version} + end; + false -> + {skip, openssl_does_not_support_version} + end; + _ -> + Config + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +init_per_testcase(TestCase, Config) -> + ct:timetrap({seconds, 10}), + special_init(TestCase, Config). + +special_init(ssl2_erlang_server_openssl_client, Config) -> + case ssl_test_lib:supports_ssl_tls_version(sslv2) of + true -> + Config; + false -> + {skip, "sslv2 not supported by openssl"} + end; +special_init(_, Config) -> + Config. + +end_per_testcase(_, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +erlang_client_bad_openssl_server() -> + [{doc,"Test what happens if openssl server sends garbage to erlang ssl client"}]. +erlang_client_bad_openssl_server(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Version = ssl_test_lib:protocol_version(Config), + Exe = "openssl", + Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-cert", CertFile, "-key", KeyFile], + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + + Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, server_sent_garbage, []}}, + {options, + [{versions, [Version]} | ClientOpts]}]), + + %% Send garbage + true = port_command(OpensslPort, ?OPENSSL_GARBAGE), + + ct:sleep(?SLEEP), + + Client0 ! server_sent_garbage, + + ssl_test_lib:check_result(Client0, true), + + ssl_test_lib:close(Client0), + + %% Make sure openssl does not hang and leave zombie process + Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, + [{versions, [Version]} | ClientOpts]}]), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + ssl_test_lib:close(Client1), + process_flag(trap_exit, false). + +%%-------------------------------------------------------------------- +ssl2_erlang_server_openssl_client() -> + [{doc,"Test that ssl v2 clients are rejected"}]. + +ssl2_erlang_server_openssl_client(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Exe = "openssl", + Args = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname) ++ ":" ++ integer_to_list(Port), + "-ssl2", "-msg"], + + OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]), + ssl_test_lib:consume_port_exit(OpenSslPort), + ssl_test_lib:check_server_alert(Server, unexpected_message), + process_flag(trap_exit, false). + + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- + +server_sent_garbage(Socket) -> + receive + server_sent_garbage -> + {error, closed} == ssl:send(Socket, "data") + + end. diff --git a/lib/ssl/test/openssl_renegotiate_SUITE.erl b/lib/ssl/test/openssl_renegotiate_SUITE.erl new file mode 100644 index 0000000000..91a8175ac6 --- /dev/null +++ b/lib/ssl/test/openssl_renegotiate_SUITE.erl @@ -0,0 +1,341 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(openssl_renegotiate_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +-define(SLEEP, 1000). +-define(OPENSSL_RENEGOTIATE, "R\n"). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +all() -> + case ssl_test_lib:openssl_sane_dtls() of + true -> + [{group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}, + {group, 'dtlsv1.2'}, + {group, 'dtlsv1'}]; + false -> + [{group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}] + end. + +groups() -> + case ssl_test_lib:openssl_sane_dtls() of + true -> + [{'tlsv1.2', [], all_versions_tests()}, + {'tlsv1.1', [], all_versions_tests()}, + {'tlsv1', [], all_versions_tests()}, + {'sslv3', [], all_versions_tests()}, + {'dtlsv1.2', [], all_versions_tests()}, + {'dtlsv1', [], all_versions_tests()} + ]; + false -> + [{'tlsv1.2', [], all_versions_tests()}, + {'tlsv1.1', [], all_versions_tests()}, + {'tlsv1', [], all_versions_tests()}, + {'sslv3', [], all_versions_tests()} + ] + end. + +all_versions_tests() -> + [ + erlang_client_openssl_server_renegotiate, + erlang_client_openssl_server_renegotiate_after_client_data, + erlang_client_openssl_server_nowrap_seqnum, + erlang_server_openssl_client_nowrap_seqnum + ]. + + +init_per_suite(Config0) -> + case os:find_executable("openssl") of + false -> + {skip, "Openssl not found"}; + _ -> + ct:pal("Version: ~p", [os:cmd("openssl version")]), + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + ssl_test_lib:make_rsa_cert(Config0) + catch _:_ -> + {skip, "Crypto did not start"} + end + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto), + ssl_test_lib:kill_openssl(). + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:supports_ssl_tls_version(GroupName) of + true -> + case ssl_test_lib:check_sane_openssl_version(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName, Config); + false -> + {skip, openssl_does_not_support_version} + end; + false -> + {skip, openssl_does_not_support_version} + end; + _ -> + Config + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +init_per_testcase(TestCase, Config) -> + ct:timetrap({seconds, 10}), + special_init(TestCase, Config). + +special_init(TestCase, Config) + when TestCase == erlang_client_openssl_server_renegotiate; + TestCase == erlang_client_openssl_server_nowrap_seqnum; + TestCase == erlang_server_openssl_client_nowrap_seqnum; + TestCase == erlang_client_openssl_server_renegotiate_after_client_data + -> + {ok, Version} = application:get_env(ssl, protocol_version), + ssl_test_lib:check_sane_openssl_renegotaite(Config, Version); +special_init(_, Config) -> + Config. + +end_per_testcase(_, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +erlang_client_openssl_server_renegotiate() -> + [{doc,"Test erlang client when openssl server issuses a renegotiate"}]. +erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + ErlData = "From erlang to openssl", + OpenSslData = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Version = ssl_test_lib:protocol_version(Config), + + Exe = "openssl", + Args = ["s_server", "-accept", integer_to_list(Port), + ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, + "-cert", CertFile, "-key", KeyFile, "-msg"], + + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + delayed_send, [[ErlData, OpenSslData]]}}, + {options, [{reuse_sessions, false} | ClientOpts]}]), + + true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), + ct:sleep(?SLEEP), + true = port_command(OpensslPort, OpenSslData), + + ssl_test_lib:check_result(Client, OpenSslData), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + ssl_test_lib:close(Client), + process_flag(trap_exit, false), + ok. +%%-------------------------------------------------------------------- +erlang_client_openssl_server_renegotiate_after_client_data() -> + [{doc,"Test erlang client when openssl server issuses a renegotiate after reading client data"}]. +erlang_client_openssl_server_renegotiate_after_client_data(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + ErlData = "From erlang to openssl", + OpenSslData = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Version = ssl_test_lib:protocol_version(Config), + + Exe = "openssl", + Args = ["s_server", "-accept", integer_to_list(Port), + ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, + "-cert", CertFile, "-key", KeyFile, "-msg"], + + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + send_wait_send, [[ErlData, OpenSslData]]}}, + {options, [{reuse_sessions, false} |ClientOpts]}]), + + true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), + ct:sleep(?SLEEP), + true = port_command(OpensslPort, OpenSslData), + + ssl_test_lib:check_result(Client, OpenSslData), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + ssl_test_lib:close(Client), + process_flag(trap_exit, false), + ok. + +%%-------------------------------------------------------------------- +erlang_client_openssl_server_nowrap_seqnum() -> + [{doc, "Test that erlang client will renegotiate session when", + "max sequence number celing is about to be reached. Although" + "in the testcase we use the test option renegotiate_at" + " to lower treashold substantially."}]. +erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + ErlData = "From erlang to openssl\n", + N = 10, + + Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Version = ssl_test_lib:protocol_version(Config), + Exe = "openssl", + Args = ["s_server", "-accept", integer_to_list(Port), + ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, + "-cert", CertFile, "-key", KeyFile, "-msg"], + + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + trigger_renegotiate, [[ErlData, N+2]]}}, + {options, [{reuse_sessions, false}, + {renegotiate_at, N} | ClientOpts]}]), + + ssl_test_lib:check_result(Client, ok), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + ssl_test_lib:close(Client), + process_flag(trap_exit, false). +%%-------------------------------------------------------------------- +erlang_server_openssl_client_nowrap_seqnum() -> + [{doc, "Test that erlang client will renegotiate session when", + "max sequence number celing is about to be reached. Although" + "in the testcase we use the test option renegotiate_at" + " to lower treashold substantially."}]. +erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + N = 10, + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + trigger_renegotiate, [[Data, N+2]]}}, + {options, [{renegotiate_at, N}, {reuse_sessions, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Version = ssl_test_lib:protocol_version(Config), + Exe = "openssl", + Args = ["s_client","-connect", ssl_test_lib:hostname_format(Hostname) ++ ":" ++ integer_to_list(Port), + ssl_test_lib:version_flag(Version), + "-msg"], + + OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), + + true = port_command(OpenSslPort, Data), + + ssl_test_lib:check_result(Server, ok), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close(Server), + ssl_test_lib:close_port(OpenSslPort), + process_flag(trap_exit, false). + + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- +delayed_send(Socket, [ErlData, OpenSslData]) -> + ct:sleep(?SLEEP), + ssl:send(Socket, ErlData), + ssl_test_lib:active_recv(Socket, length(OpenSslData)). + + +send_wait_send(Socket, [ErlData, OpenSslData]) -> + ssl:send(Socket, ErlData), + ct:sleep(?SLEEP), + ssl:send(Socket, ErlData), + ssl_test_lib:active_recv(Socket, length(OpenSslData)). + diff --git a/lib/ssl/test/openssl_server_cert_SUITE.erl b/lib/ssl/test/openssl_server_cert_SUITE.erl new file mode 100644 index 0000000000..c2af864a92 --- /dev/null +++ b/lib/ssl/test/openssl_server_cert_SUITE.erl @@ -0,0 +1,373 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(openssl_server_cert_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). +-include_lib("common_test/include/ct.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +all() -> + [ + {group, openssl_server}]. + +groups() -> + [ + {openssl_server, [], protocol_groups()}, + {'tlsv1.3', [], tls_1_3_protocol_groups()}, + {'tlsv1.2', [], pre_tls_1_3_protocol_groups()}, + {'tlsv1.1', [], pre_tls_1_3_protocol_groups()}, + {'tlsv1', [], pre_tls_1_3_protocol_groups()}, + {'sslv3', [], ssl_protocol_groups()}, + {'dtlsv1.2', [], pre_tls_1_3_protocol_groups()}, + {'dtlsv1', [], pre_tls_1_3_protocol_groups()}, + {rsa, [], all_version_tests()}, + {ecdsa, [], all_version_tests()}, + {dsa, [], all_version_tests()}, + {rsa_1_3, [], all_version_tests() ++ tls_1_3_tests()}, + %% TODO: Create proper conf of openssl server + %%++ [unsupported_sign_algo_client_auth, + %% unsupported_sign_algo_cert_client_auth]}, + {ecdsa_1_3, [], all_version_tests() ++ tls_1_3_tests()} + ]. + +protocol_groups() -> + [{group, 'tlsv1.3'}, + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}, + {group, 'dtlsv1.2'}, + {group, 'dtlsv1'} + ]. + +ssl_protocol_groups() -> + [{group, rsa}, + {group, dsa}]. + +pre_tls_1_3_protocol_groups() -> + [{group, rsa}, + {group, ecdsa}, + {group, dsa}]. + +tls_1_3_protocol_groups() -> + [{group, rsa_1_3}, + {group, ecdsa_1_3}]. + +tls_1_3_tests() -> + [ + hello_retry_request, + custom_groups, + hello_retry_client_auth, + hello_retry_client_auth_empty_cert_accepted, + hello_retry_client_auth_empty_cert_rejected + ]. + +all_version_tests() -> + [ + no_auth, + auth, + missing_root_cert_no_auth + %%invalid_signature_client + ]. + +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + Config + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:unload(ssl), + application:stop(crypto). + +init_per_group(openssl_server, Config0) -> + Config = proplists:delete(server_type, proplists:delete(client_type, Config0)), + [{client_type, erlang}, {server_type, openssl} | Config]; +init_per_group(rsa = Group, Config0) -> + Config = ssl_test_lib:make_rsa_cert(Config0), + COpts = proplists:get_value(client_rsa_opts, Config), + SOpts = proplists:get_value(server_rsa_opts, Config), + %% Make sure _rsa* suite is choosen by ssl_test_lib:start_server + Version = proplists:get_value(version,Config), + Ciphers = ssl_cert_tests:test_ciphers(fun(dhe_rsa) -> + true; + (ecdhe_rsa) -> + true; + (_) -> + false + end, Version), + case Ciphers of + [_|_] -> + [{cert_key_alg, rsa} | + lists:delete(cert_key_alg, + [{client_cert_opts, [{ciphers, Ciphers} | COpts]}, + {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, + lists:delete(client_cert_opts, Config))])]; + [] -> + {skip, {no_sup, Group, Version}} + end; +init_per_group(rsa_1_3 = Group, Config0) -> + Config = ssl_test_lib:make_rsa_cert(Config0), + COpts = proplists:get_value(client_rsa_opts, Config), + SOpts = proplists:get_value(server_rsa_opts, Config), + %% Make sure _rsa* suite is choosen by ssl_test_lib:start_server + Version = proplists:get_value(version,Config), + Ciphers = ssl_cert_tests:test_ciphers(undefined, Version), + case Ciphers of + [_|_] -> + [{cert_key_alg, rsa} | + lists:delete(cert_key_alg, + [{client_cert_opts, [{ciphers, Ciphers} | COpts]}, + {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, + lists:delete(client_cert_opts, Config))])]; + [] -> + {skip, {no_sup, Group, Version}} + end; +init_per_group(ecdsa = Group, Config0) -> + PKAlg = crypto:supports(public_keys), + case lists:member(ecdsa, PKAlg) andalso (lists:member(ecdh, PKAlg) orelse + lists:member(dh, PKAlg)) of + true -> + Config = ssl_test_lib:make_ecdsa_cert(Config0), + COpts = proplists:get_value(client_ecdsa_opts, Config), + SOpts = proplists:get_value(server_ecdsa_opts, Config), + %% Make sure ecdh* suite is choosen by ssl_test_lib:start_server + Version = proplists:get_value(version,Config), + Ciphers = ssl_cert_tests:test_ciphers(fun(ecdh_ecdsa) -> + true; + (ecdhe_ecdsa) -> + true; + (_) -> + false + end, Version), + case Ciphers of + [_|_] -> + [{cert_key_alg, ecdsa} | + lists:delete(cert_key_alg, + [{client_cert_opts, [{ciphers, Ciphers} | COpts]}, + {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, + lists:delete(client_cert_opts, Config))] + )]; + [] -> + {skip, {no_sup, Group, Version}} + end; + false -> + {skip, "Missing EC crypto support"} + end; +init_per_group(ecdsa_1_3 = Group, Config0) -> + PKAlg = crypto:supports(public_keys), + case lists:member(ecdsa, PKAlg) andalso (lists:member(ecdh, PKAlg) orelse + lists:member(dh, PKAlg)) of + true -> + Config = ssl_test_lib:make_ecdsa_cert(Config0), + COpts = proplists:get_value(client_ecdsa_opts, Config), + SOpts = proplists:get_value(server_ecdsa_opts, Config), + %% Make sure ecdh* suite is choosen by ssl_test_lib:start_server + Version = proplists:get_value(version,Config), + Ciphers = ssl_cert_tests:test_ciphers(undefined, Version), + case Ciphers of + [_|_] -> + [{cert_key_alg, ecdsa} | + lists:delete(cert_key_alg, + [{client_cert_opts, [{ciphers, Ciphers} | COpts]}, + {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, + lists:delete(client_cert_opts, Config))] + )]; + [] -> + {skip, {no_sup, Group, Version}} + end; + false -> + {skip, "Missing EC crypto support"} + end; +init_per_group(Group, Config0) when Group == dsa -> + PKAlg = crypto:supports(public_keys), + case lists:member(dss, PKAlg) andalso lists:member(dh, PKAlg) of + true -> + Config = ssl_test_lib:make_dsa_cert(Config0), + COpts = proplists:get_value(client_dsa_opts, Config), + SOpts = proplists:get_value(server_dsa_opts, Config), + %% Make sure dhe_dss* suite is choosen by ssl_test_lib:start_server + Version = proplists:get_value(version,Config), + Ciphers = ssl_cert_tests:test_ciphers(fun(dh_dss) -> + true; + (dhe_dss) -> + true; + (_) -> + false + end, Version), + case Ciphers of + [_|_] -> + [{cert_key_alg, dsa} | + lists:delete(cert_key_alg, + [{client_cert_opts, [{ciphers, Ciphers} | COpts]}, + {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, + lists:delete(client_cert_opts, Config))])]; + [] -> + {skip, {no_sup, Group, Version}} + end; + false -> + {skip, "Missing DSS crypto support"} + end; +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:check_sane_openssl_version(GroupName) of + true -> + [{version, GroupName} + | ssl_test_lib:init_tls_version(GroupName, Config)]; + false -> + {skip, "Missing openssl support"} + end; + _ -> + ssl:start(), + Config + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +init_per_testcase(_TestCase, Config) -> + ssl_test_lib:ct_log_supported_protocol_versions(Config), + ct:timetrap({seconds, 10}), + Config. + +end_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +no_auth() -> + ssl_cert_tests:no_auth(). + +no_auth(Config) -> + ssl_cert_tests:no_auth(Config). +%%-------------------------------------------------------------------- +auth() -> + ssl_cert_tests:auth(). +auth(Config) -> + ssl_cert_tests:auth(Config). +%%-------------------------------------------------------------------- +client_auth_empty_cert_accepted() -> + ssl_cert_tests:client_auth_empty_cert_accepted(). +client_auth_empty_cert_accepted(Config) -> + ssl_cert_tests:client_auth_empty_cert_accepted(Config). +%%-------------------------------------------------------------------- +client_auth_empty_cert_rejected() -> + ssl_cert_tests:client_auth_empty_cert_rejected(). +client_auth_empty_cert_rejected(Config) -> + ssl_cert_tests:client_auth_empty_cert_rejected(Config). +%%-------------------------------------------------------------------- +client_auth_partial_chain() -> + ssl_cert_tests:client_auth_partial_chain(). +client_auth_partial_chain(Config) when is_list(Config) -> + ssl_cert_tests:client_auth_partial_chain(Config). + +%%-------------------------------------------------------------------- +client_auth_allow_partial_chain() -> + ssl_cert_tests:client_auth_allow_partial_chain(). +client_auth_allow_partial_chain(Config) when is_list(Config) -> + ssl_cert_tests:client_auth_allow_partial_chain(Config). +%%-------------------------------------------------------------------- +client_auth_do_not_allow_partial_chain() -> + ssl_cert_tests:client_auth_do_not_allow_partial_chain(). +client_auth_do_not_allow_partial_chain(Config) when is_list(Config) -> + ssl_cert_tests:client_auth_do_not_allow_partial_chain(Config). + +%%-------------------------------------------------------------------- +client_auth_partial_chain_fun_fail() -> + ssl_cert_tests:client_auth_partial_chain_fun_fail(). +client_auth_partial_chain_fun_fail(Config) when is_list(Config) -> + ssl_cert_tests:client_auth_partial_chain_fun_fail(Config). + +%%-------------------------------------------------------------------- +missing_root_cert_no_auth() -> + ssl_cert_tests:missing_root_cert_no_auth(). +missing_root_cert_no_auth(Config) when is_list(Config) -> + ssl_cert_tests:missing_root_cert_no_auth(Config). + +%%-------------------------------------------------------------------- +invalid_signature_client() -> + ssl_cert_tests:invalid_signature_client(). +invalid_signature_client(Config) when is_list(Config) -> + ssl_cert_tests:invalid_signature_client(Config). +%%-------------------------------------------------------------------- +invalid_signature_server() -> + ssl_cert_tests:invalid_signature_client(). +invalid_signature_server(Config) when is_list(Config) -> + ssl_cert_tests:invalid_signature_client(Config). + +%%-------------------------------------------------------------------- +%% TLS 1.3 Test Cases ------------------------------------------------ +%%-------------------------------------------------------------------- +hello_retry_request() -> + ssl_cert_tests:hello_retry_request(). +hello_retry_request(Config) -> + ssl_cert_tests:hello_retry_request(Config). +%%-------------------------------------------------------------------- +custom_groups() -> + ssl_cert_tests:custom_groups(). +custom_groups(Config) -> + ssl_cert_tests:custom_groups(Config). +unsupported_sign_algo_cert_client_auth() -> + ssl_cert_tests:unsupported_sign_algo_cert_client_auth(). +unsupported_sign_algo_cert_client_auth(Config) -> + ssl_cert_tests:unsupported_sign_algo_cert_client_auth(Config). +unsupported_sign_algo_client_auth() -> + ssl_cert_tests:unsupported_sign_algo_client_auth(). +unsupported_sign_algo_client_auth(Config) -> + ssl_cert_tests:unsupported_sign_algo_client_auth(Config). +%%-------------------------------------------------------------------- +hello_retry_client_auth() -> + ssl_cert_tests:hello_retry_client_auth(). +hello_retry_client_auth(Config) -> + ssl_cert_tests:hello_retry_client_auth(Config). +%%-------------------------------------------------------------------- +hello_retry_client_auth_empty_cert_accepted() -> + ssl_cert_tests:hello_retry_client_auth_empty_cert_accepted(). +hello_retry_client_auth_empty_cert_accepted(Config) -> + ssl_cert_tests:hello_retry_client_auth_empty_cert_accepted(Config). +%%-------------------------------------------------------------------- +hello_retry_client_auth_empty_cert_rejected() -> + ssl_cert_tests:hello_retry_client_auth_empty_cert_rejected(). +hello_retry_client_auth_empty_cert_rejected(Config) -> + ssl_cert_tests:hello_retry_client_auth_empty_cert_rejected(Config). diff --git a/lib/ssl/test/openssl_session_SUITE.erl b/lib/ssl/test/openssl_session_SUITE.erl new file mode 100644 index 0000000000..24dcaa7817 --- /dev/null +++ b/lib/ssl/test/openssl_session_SUITE.erl @@ -0,0 +1,262 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2018. 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(openssl_session_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +-define(SLEEP, 1000). +-define(EXPIRE, 10). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +all() -> + case ssl_test_lib:openssl_sane_dtls() of + true -> + [{group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}, + {group, 'dtlsv1.2'}, + {group, 'dtlsv1'}]; + false -> + [{group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}] + end. + +groups() -> + case ssl_test_lib:openssl_sane_dtls() of + true -> + [{'tlsv1.2', [], tests()}, + {'tlsv1.1', [], tests()}, + {'tlsv1', [], tests()}, + {'sslv3', [], tests()}, + {'dtlsv1.2', [], dtls_tests()}, + {'dtlsv1', [], dtls_tests()} + ]; + false -> + [{'tlsv1.2', [], tests()}, + {'tlsv1.1', [], tests()}, + {'tlsv1', [], tests()}, + {'sslv3', [], tests()} + ] + end. + +tests() -> + [ + reuse_session_erlang_server, + reuse_session_erlang_client + ]. + +dtls_tests() -> + [ + reuse_session_erlang_server + ]. + + +init_per_suite(Config0) -> + case os:find_executable("openssl") of + false -> + {skip, "Openssl not found"}; + _ -> + ct:pal("Version: ~p", [os:cmd("openssl version")]), + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + ssl_test_lib:make_rsa_cert(Config0) + catch _:_ -> + {skip, "Crypto did not start"} + end + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto), + ssl_test_lib:kill_openssl(). + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:supports_ssl_tls_version(GroupName) of + true -> + case ssl_test_lib:check_sane_openssl_version(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName, Config); + false -> + {skip, openssl_does_not_support_version} + end; + false -> + {skip, openssl_does_not_support_version} + end; + _ -> + ssl:start(), + Config + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +init_per_testcase(reuse_session_erlang_client, Config) -> + ct:timetrap(?EXPIRE * 1000 * 5), + ssl:stop(), + application:load(ssl), + application:set_env(ssl, session_lifetime, ?EXPIRE), + ssl:start(), + Config; + +init_per_testcase(TestCase, Config) -> + ct:timetrap({seconds, 10}), + Config. + +end_per_testcase(reuse_session_erlang_client, Config) -> + application:unset_env(ssl, session_lifetime), + Config; +end_per_testcase(_, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +%%-------------------------------------------------------------------- +reuse_session_erlang_server() -> + [{doc, "Test erlang server with openssl client that reconnects with the" + "same session id, to test reusing of sessions."}]. +reuse_session_erlang_server(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, active_recv, [length(Data)]}}, + {reconnect_times, 5}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Version = ssl_test_lib:protocol_version(Config), + + Exe = "openssl", + Args = ["s_client", "-connect", ssl_test_lib:hostname_format(Hostname) + ++ ":" ++ integer_to_list(Port), + ssl_test_lib:version_flag(Version), + "-reconnect"], + + OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), + + true = port_command(OpenSslPort, Data), + + ssl_test_lib:check_result(Server, Data), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close(Server), + ssl_test_lib:close_port(OpenSslPort). + +%%-------------------------------------------------------------------- + +reuse_session_erlang_client() -> + [{doc, "Test erlang ssl client that wants to reuse sessions"}]. +reuse_session_erlang_client(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Port = ssl_test_lib:inet_port(node()), + CertFile = proplists:get_value(certfile, ServerOpts), + CACertFile = proplists:get_value(cacertfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + + Exe = "openssl", + Args = ["s_server", "-accept", integer_to_list(Port), + "-cert", CertFile,"-key", KeyFile, "-CAfile", CACertFile], + + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, tls), + + Client0 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, save}, {verify, verify_peer}| ClientOpts]}]), + + SID = receive + {Client0, Id0} -> + Id0 + end, + + ssl_test_lib:close(Client0), + + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_session, SID} | ClientOpts]}]), + receive + {Client1, SID} -> + ok + after ?SLEEP -> + ct:fail(session_not_reused) + end, + + + ssl_test_lib:close(Client1), + %% Make sure session is unregistered due to expiration + ct:sleep(20000), + + Client2 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, ClientOpts}]), + receive + {Client2, ID} -> + case ID of + SID -> + ct:fail(expired_session_reused); + _ -> + ok + end + end, + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + ssl_test_lib:close(Client2). + + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- diff --git a/lib/ssl/test/openssl_sni_SUITE.erl b/lib/ssl/test/openssl_sni_SUITE.erl new file mode 100644 index 0000000000..26f08e36c0 --- /dev/null +++ b/lib/ssl/test/openssl_sni_SUITE.erl @@ -0,0 +1,251 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(openssl_sni_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). + +-define(OPENSSL_QUIT, "Q\n"). +-define(OPENSSL_RENEGOTIATE, "R\n"). +-define(SLEEP, 1000). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +all() -> + %% Note: SNI not supported in sslv3 + case ssl_test_lib:openssl_sane_dtls() of + true -> + [{group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'} + %% Seems broken in openssl + %%{group, 'dtlsv1.2'}, + %%{group, 'dtlsv1'} + ]; + false -> + [{group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}] + end. + +groups() -> + case ssl_test_lib:openssl_sane_dtls() of + true -> + [{'tlsv1.2', [], sni_tests()}, + {'tlsv1.1', [], sni_tests()}, + {'tlsv1', [], sni_tests()} + %% Seems broken in openssl + %%{'dtlsv1.2', [], sni_tests()}, + %%{'dtlsv1', [], sni_tests()} + ]; + false -> + [{'tlsv1.2', [], sni_tests()}, + {'tlsv1.1', [], sni_tests()}, + {'tlsv1', [], sni_tests()} + ] + end. + +sni_tests() -> + [erlang_server_openssl_client_sni_match, + erlang_server_openssl_client_sni_match_fun, + erlang_server_openssl_client_sni_no_match, + erlang_server_openssl_client_sni_no_match_fun, + erlang_server_openssl_client_sni_no_header, + erlang_server_openssl_client_sni_no_header_fun]. + +init_per_suite(Config0) -> + case os:find_executable("openssl") of + false -> + {skip, "Openssl not found"}; + _ -> + case check_openssl_sni_support(Config0) of + {skip, _} = Skip -> + Skip; + _ -> + ct:pal("Version: ~p", [os:cmd("openssl version")]), + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + Config = ssl_test_lib:make_rsa_cert(Config0), + RsaOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + [{sni_server_opts, [{sni_hosts, + [{"a.server", [ + {certfile, proplists:get_value(certfile, RsaOpts)}, + {keyfile, proplists:get_value(keyfile, RsaOpts)} + ]}, + {"b.server", [ + {certfile, proplists:get_value(certfile, RsaOpts)}, + {keyfile, proplists:get_value(keyfile, RsaOpts)} + ]} + ]}]} | Config] + catch _:_ -> + {skip, "Crypto did not start"} + end + end + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto), + ssl_test_lib:kill_openssl(). + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:supports_ssl_tls_version(GroupName) of + true -> + case ssl_test_lib:check_sane_openssl_version(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName, Config); + false -> + {skip, openssl_does_not_support_version} + end; + false -> + {skip, openssl_does_not_support_version} + end; + _ -> + Config + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +init_per_testcase(_TestCase, Config) -> + ct:timetrap({seconds, 10}), + Config. + + +end_per_testcase(_, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +erlang_server_openssl_client_sni_no_header(Config) when is_list(Config) -> + erlang_server_openssl_client_sni_test(Config, undefined, undefined, "server Peer cert"). + +erlang_server_openssl_client_sni_no_header_fun(Config) when is_list(Config) -> + erlang_server_openssl_client_sni_test_sni_fun(Config, undefined, undefined, "server Peer cert"). + +erlang_server_openssl_client_sni_match(Config) when is_list(Config) -> + erlang_server_openssl_client_sni_test(Config, "a.server", "a.server", "server Peer cert"). + +erlang_server_openssl_client_sni_match_fun(Config) when is_list(Config) -> + erlang_server_openssl_client_sni_test_sni_fun(Config, "a.server", "a.server", "server Peer cert"). + +erlang_server_openssl_client_sni_no_match(Config) when is_list(Config) -> + erlang_server_openssl_client_sni_test(Config, "c.server", undefined, "server Peer cert"). + +erlang_server_openssl_client_sni_no_match_fun(Config) when is_list(Config) -> + erlang_server_openssl_client_sni_test_sni_fun(Config, "c.server", undefined, "server Peer cert"). + + +erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) -> + Version = ssl_test_lib:protocol_version(Config), + ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", + [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]), + ServerOptions = proplists:get_value(sni_server_opts, Config) ++ proplists:get_value(server_rsa_opts, Config), + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, {mfa, {?MODULE, send_and_hostname, []}}, + {options, ServerOptions}]), + Port = ssl_test_lib:inet_port(Server), + Exe = "openssl", + ClientArgs = case SNIHostname of + undefined -> + openssl_client_args(Version, Hostname,Port); + _ -> + openssl_client_args(Version, Hostname, Port, SNIHostname) + end, + ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs), + + ssl_test_lib:check_result(Server, ExpectedSNIHostname), + ssl_test_lib:close_port(ClientPort), + ssl_test_lib:close(Server), + ok. + + +erlang_server_openssl_client_sni_test_sni_fun(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) -> + Version = ssl_test_lib:protocol_version(Config), + ct:log("Start running handshake for sni_fun, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", + [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]), + [{sni_hosts, ServerSNIConf}] = proplists:get_value(sni_server_opts, Config), + SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, undefined) end, + ServerOptions = proplists:get_value(server_rsa_opts, Config) ++ [{sni_fun, SNIFun}], + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, {mfa, {?MODULE, send_and_hostname, []}}, + {options, ServerOptions}]), + Port = ssl_test_lib:inet_port(Server), + Exe = "openssl", + ClientArgs = case SNIHostname of + undefined -> + openssl_client_args(Version, Hostname,Port); + _ -> + openssl_client_args(Version, Hostname, Port, SNIHostname) + end, + + ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs), + + ssl_test_lib:check_result(Server, ExpectedSNIHostname), + ssl_test_lib:close_port(ClientPort), + ssl_test_lib:close(Server). + +send_and_hostname(SSLSocket) -> + ssl:send(SSLSocket, "OK"), + case ssl:connection_information(SSLSocket, [sni_hostname]) of + {ok, []} -> + undefined; + {ok, [{sni_hostname, Hostname}]} -> + Hostname + end. + +openssl_client_args(Version, Hostname, Port) -> + ["s_client", "-connect", Hostname ++ ":" ++ integer_to_list(Port), ssl_test_lib:version_flag(Version)]. + +openssl_client_args(Version, Hostname, Port, ServerName) -> + ["s_client", "-connect", Hostname ++ ":" ++ + integer_to_list(Port), ssl_test_lib:version_flag(Version), "-servername", ServerName]. + +check_openssl_sni_support(Config) -> + HelpText = os:cmd("openssl s_client --help"), + case ssl_test_lib:is_sane_oppenssl_client() of + true -> + case string:str(HelpText, "-servername") of + 0 -> + {skip, "Current openssl doesn't support SNI"}; + _ -> + Config + end; + false -> + {skip, "Current openssl doesn't support SNI or extension handling is flawed"} + end. diff --git a/lib/ssl/test/openssl_tls_1_3_version_SUITE.erl b/lib/ssl/test/openssl_tls_1_3_version_SUITE.erl new file mode 100644 index 0000000000..8a2692ec1d --- /dev/null +++ b/lib/ssl/test/openssl_tls_1_3_version_SUITE.erl @@ -0,0 +1,172 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(openssl_tls_1_3_version_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +all() -> + [ + %%{group, openssl_server}, + {group, openssl_client} + ]. + +groups() -> + [ + %%{openssl_server, [{group, 'tlsv1.3'}]}, + {openssl_client, [{group, 'tlsv1.3'}]}, + {'tlsv1.3', [], cert_groups()}, + {rsa, [], tests()}, + {ecdsa, [], tests()} + ]. + +cert_groups() -> + [{group, rsa}, + {group, ecdsa}]. + +tests() -> + [tls13_client_tls12_server, + %%tls13_client_with_ext_tls12_server, + tls12_client_tls13_server]. + +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + case ssl_test_lib:check_sane_openssl_version('tlsv1.3') of + true -> + ssl_test_lib:clean_start(), + Config; + false -> + {skip, openssl_does_not_support_version} + end + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). + +init_per_group(openssl_client, Config0) -> + Config = proplists:delete(server_type, proplists:delete(client_type, Config0)), + [{client_type, openssl}, {server_type, erlang} | Config]; +init_per_group(openssl_server, Config0) -> + Config = proplists:delete(server_type, proplists:delete(client_type, Config0)), + [{client_type, erlang}, {server_type, openssl} | Config]; +init_per_group(rsa, Config0) -> + Config = ssl_test_lib:make_rsa_cert(Config0), + COpts = proplists:get_value(client_rsa_opts, Config), + SOpts = proplists:get_value(server_rsa_opts, Config), + [{client_cert_opts, COpts}, {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, lists:delete(client_cert_opts, Config))]; +init_per_group(ecdsa, Config0) -> + PKAlg = crypto:supports(public_keys), + case lists:member(ecdsa, PKAlg) andalso + (lists:member(ecdh, PKAlg) orelse lists:member(dh, PKAlg)) of + true -> + Config = ssl_test_lib:make_ecdsa_cert(Config0), + COpts = proplists:get_value(client_ecdsa_opts, Config), + SOpts = proplists:get_value(server_ecdsa_opts, Config), + [{client_cert_opts, COpts}, {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, lists:delete(client_cert_opts, Config))]; + false -> + {skip, "Missing EC crypto support"} + end; +init_per_group(GroupName, Config) -> + ssl_test_lib:clean_tls_version(Config), + case ssl_test_lib:is_tls_version(GroupName) andalso + ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName, Config); + _ -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl:start(), + Config; + false -> + {skip, "Missing crypto support"} + end + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +tls13_client_tls12_server() -> + [{doc,"Test that a TLS 1.3 client can connect to a TLS 1.2 server."}]. + +tls13_client_tls12_server(Config) when is_list(Config) -> + ClientOpts = [{versions, + ['tlsv1.3', 'tlsv1.2']} | ssl_test_lib:ssl_options(client_cert_opts, Config)], + ServerOpts = [{versions, + ['tlsv1.1', 'tlsv1.2']} | ssl_test_lib:ssl_options(server_cert_opts, Config)], + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +%% tls13_client_with_ext_tls12_server() -> +%% [{doc,"Test basic connection between TLS 1.2 server and TLS 1.3 client when " +%% "client has TLS 1.3 specsific extensions"}]. + +%% tls13_client_with_ext_tls12_server(Config) -> +%% ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), +%% ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + +%% {ServerOpts, ClientOpts} = +%% case proplists:get_value(client_type) of +%% erlang -> +%% {[{versions, ['tlsv1.2']}|ServerOpts0], +%% [{versions, ['tlsv1.2','tlsv1.3']}, +%% {signature_algs_cert, [ecdsa_secp384r1_sha384, +%% ecdsa_secp256r1_sha256, +%% rsa_pss_rsae_sha256, +%% rsa_pkcs1_sha256, +%% {sha256,rsa},{sha256,dsa}]}|ClientOpts0]}; +%% openssl -> + + +%% ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +tls12_client_tls13_server() -> + [{doc,"Test that a TLS 1.2 client can connect to a TLS 1.3 server."}]. + +tls12_client_tls13_server(Config) when is_list(Config) -> + ClientOpts = [{versions, + ['tlsv1.1', 'tlsv1.2']} | ssl_test_lib:ssl_options(client_cert_opts, Config)], + ServerOpts = [{versions, + ['tlsv1.3', 'tlsv1.2']} | ssl_test_lib:ssl_options(server_cert_opts, Config)], + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + diff --git a/lib/ssl/test/property_test/ssl_eqc_handshake.erl b/lib/ssl/test/property_test/ssl_eqc_handshake.erl index 38a4b7fb11..2ceb540e15 100644 --- a/lib/ssl/test/property_test/ssl_eqc_handshake.erl +++ b/lib/ssl/test/property_test/ssl_eqc_handshake.erl @@ -119,19 +119,27 @@ tls_msg(Version) -> %% client_hello(?'TLS_v1.3' = Version) -> #client_hello{session_id = session_id(), - client_version = ?'TLS_v1.2', - cipher_suites = cipher_suites(Version), + client_version = ?'TLS_v1.2', + cipher_suites = cipher_suites(Version), + compression_methods = compressions(Version), + random = client_random(Version), + extensions = client_hello_extensions(Version) + }; +client_hello(Version) -> + #client_hello{session_id = session_id(), + client_version = Version, + cipher_suites = cipher_suites(Version), compression_methods = compressions(Version), random = client_random(Version), extensions = client_hello_extensions(Version) }; -client_hello(Version) -> +client_hello(?'SSL_v3' = Version) -> #client_hello{session_id = session_id(), client_version = Version, cipher_suites = cipher_suites(Version), compression_methods = compressions(Version), random = client_random(Version), - extensions = client_hello_extensions(Version) + extensions = ssl_handshake:empty_extensions(Version, client_hello) }. server_hello(?'TLS_v1.3' = Version) -> @@ -142,6 +150,14 @@ server_hello(?'TLS_v1.3' = Version) -> compression_method = compression(Version), extensions = server_hello_extensions(Version) }; +server_hello(?'SSL_v3' = Version) -> + #server_hello{server_version = Version, + session_id = session_id(), + random = server_random(Version), + cipher_suite = cipher_suite(Version), + compression_method = compression(Version), + extensions = ssl_handshake:empty_extensions(Version, server_hello) + }; server_hello(Version) -> #server_hello{server_version = Version, session_id = session_id(), @@ -291,7 +307,7 @@ pre_shared_keyextension() -> %% | | | %% | signature_algorithms_cert (RFC 8446) | CH, CR | %% +--------------------------------------------------+-------------+ -extensions(?'TLS_v1.3' = Version, client_hello) -> +extensions(?'TLS_v1.3' = Version, MsgType = client_hello) -> ?LET({ ServerName, %% MaxFragmentLength, @@ -306,8 +322,8 @@ extensions(?'TLS_v1.3' = Version, client_hello) -> %% ServerCertificateType, %% Padding, KeyShare, - %% PreSharedKey, - %% PSKKeyExchangeModes, + PreSharedKey, + PSKKeyExchangeModes, %% EarlyData, %% Cookie, SupportedVersions, @@ -328,9 +344,9 @@ extensions(?'TLS_v1.3' = Version, client_hello) -> %% oneof([client_cert_type(), undefined]), %% oneof([server_cert_type(), undefined]), %% oneof([padding(), undefined]), - oneof([key_share(client_hello), undefined]), - %% oneof([pre_shared_key(), undefined]), - %% oneof([psk_key_exchange_modes(), undefined]), + oneof([key_share(MsgType), undefined]), + oneof([pre_shared_key(MsgType), undefined]), + oneof([psk_key_exchange_modes(), undefined]), %% oneof([early_data(), undefined]), %% oneof([cookie(), undefined]), oneof([client_hello_versions(Version)]), @@ -357,8 +373,8 @@ extensions(?'TLS_v1.3' = Version, client_hello) -> %% server_cert_type => ServerCertificateType, %% padding => Padding, key_share => KeyShare, - %% pre_shared_key => PreSharedKey, - %% psk_key_exhange_modes => PSKKeyExchangeModes, + pre_shared_key => PreSharedKey, + psk_key_exchange_modes => PSKKeyExchangeModes, %% early_data => EarlyData, %% cookie => Cookie, client_hello_versions => SupportedVersions, @@ -401,15 +417,15 @@ extensions(Version, client_hello) -> srp => SRP %% renegotiation_info => RenegotiationInfo })); -extensions(?'TLS_v1.3' = Version, server_hello) -> +extensions(?'TLS_v1.3' = Version, MsgType = server_hello) -> ?LET({ KeyShare, - %% PreSharedKeys, + PreSharedKey, SupportedVersions }, { - oneof([key_share(server_hello), undefined]), - %% oneof([pre_shared_keys(), undefined]), + oneof([key_share(MsgType), undefined]), + oneof([pre_shared_key(MsgType), undefined]), oneof([server_hello_selected_version()]) }, maps:filter(fun(_, undefined) -> @@ -419,7 +435,7 @@ extensions(?'TLS_v1.3' = Version, server_hello) -> end, #{ key_share => KeyShare, - %% pre_shared_keys => PreSharedKeys, + pre_shared_key => PreSharedKey, server_hello_selected_version => SupportedVersions })); extensions(Version, server_hello) -> @@ -810,3 +826,58 @@ group_list(N, Pool, Acc) -> R = rand:uniform(length(Pool)), G = lists:nth(R, Pool), group_list(N - 1, Pool -- [G], [G|Acc]). + + +ke_modes() -> + oneof([[psk_ke],[psk_dhe_ke],[psk_ke,psk_dhe_ke]]). + +psk_key_exchange_modes() -> + ?LET(KEModes, ke_modes(), + #psk_key_exchange_modes{ + ke_modes = KEModes}). + +pre_shared_key(client_hello) -> + ?LET(OfferedPsks, offered_psks(), + #pre_shared_key_client_hello{ + offered_psks = OfferedPsks}); +pre_shared_key(server_hello) -> + ?LET(SelectedIdentity, selected_identity(), + #pre_shared_key_server_hello{ + selected_identity = SelectedIdentity}). + +selected_identity() -> + rand:uniform(32). + +offered_psks() -> + ?LET(Size, choose(1,5), + #offered_psks{ + identities = psk_identities(Size), + binders = psk_binders(Size)}). + +psk_identities(Size) -> + psk_identities(Size, []). +%% +psk_identities(0, Acc) -> + Acc; +psk_identities(N, Acc) -> + psk_identities(N - 1, [psk_identity()|Acc]). + +psk_identity() -> + Len = rand:uniform(32), + Identity = crypto:strong_rand_bytes(Len), + Age = crypto:strong_rand_bytes(4), + #psk_identity{ + identity = Identity, + obfuscated_ticket_age = Age}. + +psk_binders(Size) -> + psk_binders(Size, []). +%% +psk_binders(0, Acc) -> + Acc; +psk_binders(N, Acc) -> + psk_binders(N - 1, [psk_binder()|Acc]). + +psk_binder() -> + Len = rand:uniform(224) + 31, + crypto:strong_rand_bytes(Len). diff --git a/lib/ssl/test/ssl_ECC_openssl_SUITE.erl b/lib/ssl/test/ssl_ECC_openssl_SUITE.erl index 68d4e910fd..787c08a517 100644 --- a/lib/ssl/test/ssl_ECC_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_ECC_openssl_SUITE.erl @@ -67,7 +67,8 @@ init_per_suite(Config0) -> end_per_suite(_Config) -> application:stop(ssl), - application:stop(crypto). + application:stop(crypto), + ssl_test_lib:kill_openssl(). %%-------------------------------------------------------------------- init_per_group(GroupName, Config) -> diff --git a/lib/ssl/test/ssl_alert_SUITE.erl b/lib/ssl/test/ssl_alert_SUITE.erl new file mode 100644 index 0000000000..cc0b636580 --- /dev/null +++ b/lib/ssl/test/ssl_alert_SUITE.erl @@ -0,0 +1,100 @@ +%% +%% Copyright Ericsson AB 2019-2019. 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(ssl_alert_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +-include_lib("ssl/src/ssl_alert.hrl"). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +all() -> + [ + alerts, + alert_details, + alert_details_not_too_big + ]. + +init_per_testcase(_TestCase, Config) -> + ct:timetrap({seconds, 5}), + Config. + +end_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +alerts() -> + [{doc, "Test ssl_alert:alert_txt/1"}]. +alerts(Config) when is_list(Config) -> + Descriptions = [?CLOSE_NOTIFY, ?UNEXPECTED_MESSAGE, ?BAD_RECORD_MAC, + ?DECRYPTION_FAILED_RESERVED, ?RECORD_OVERFLOW, ?DECOMPRESSION_FAILURE, + ?HANDSHAKE_FAILURE, ?BAD_CERTIFICATE, ?UNSUPPORTED_CERTIFICATE, + ?CERTIFICATE_REVOKED,?CERTIFICATE_EXPIRED, ?CERTIFICATE_UNKNOWN, + ?ILLEGAL_PARAMETER, ?UNKNOWN_CA, ?ACCESS_DENIED, ?DECODE_ERROR, + ?DECRYPT_ERROR, ?EXPORT_RESTRICTION, ?PROTOCOL_VERSION, + ?INSUFFICIENT_SECURITY, ?INTERNAL_ERROR, ?USER_CANCELED, + ?NO_RENEGOTIATION, ?UNSUPPORTED_EXTENSION, ?CERTIFICATE_UNOBTAINABLE, + ?UNRECOGNISED_NAME, ?BAD_CERTIFICATE_STATUS_RESPONSE, + ?BAD_CERTIFICATE_HASH_VALUE, ?UNKNOWN_PSK_IDENTITY, + 255 %% Unsupported/unknow alert will result in a description too + ], + Alerts = [?ALERT_REC(?WARNING, ?CLOSE_NOTIFY) | + [?ALERT_REC(?FATAL, Desc) || Desc <- Descriptions]], + lists:foreach(fun(Alert) -> + try ssl_alert:alert_txt(Alert) + catch + C:E:T -> + ct:fail({unexpected, {C, E, T}}) + end + end, Alerts). +%%-------------------------------------------------------------------- +alert_details() -> + [{doc, "Test that ssl_alert:alert_txt/1 result contains extendend error description"}]. +alert_details(Config) when is_list(Config) -> + Unique = make_ref(), + UniqueStr = lists:flatten(io_lib:format("~w", [Unique])), + Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY, Unique), + case string:str(ssl_alert:alert_txt(Alert), UniqueStr) of + 0 -> + ct:fail(error_details_missing); + _ -> + ok + end. + +%%-------------------------------------------------------------------- +alert_details_not_too_big() -> + [{doc, "Test that ssl_alert:alert_txt/1 limits printed depth of extended error description"}]. +alert_details_not_too_big(Config) when is_list(Config) -> + Reason = lists:duplicate(10, lists:duplicate(10, lists:duplicate(10, {some, data}))), + Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY, Reason), + case length(ssl_alert:alert_txt(Alert)) < 1000 of + true -> + ok; + false -> + ct:fail(ssl_alert_text_too_big) + end. diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_SUITE.erl index dfc780479e..82a49e1469 100644 --- a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_alpn_SUITE.erl @@ -19,7 +19,7 @@ %% %% --module(ssl_alpn_handshake_SUITE). +-module(ssl_alpn_SUITE). %% Note: This directive should only be used in test suites. -compile(export_all). @@ -32,7 +32,9 @@ %%-------------------------------------------------------------------- all() -> - [{group, 'tlsv1.2'}, + [ + {group, 'tlsv1.3'}, + {group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, {group, 'sslv3'}, @@ -42,12 +44,13 @@ all() -> groups() -> [ - {'tlsv1.2', [], alpn_tests()}, - {'tlsv1.1', [], alpn_tests()}, - {'tlsv1', [], alpn_tests()}, + {'tlsv1.3', [], alpn_tests() -- [client_renegotiate, session_reused]}, + {'tlsv1.2', [], alpn_tests() ++ alpn_npn_coexist()}, + {'tlsv1.1', [], alpn_tests() ++ alpn_npn_coexist()}, + {'tlsv1', [], alpn_tests() ++ alpn_npn_coexist()}, {'sslv3', [], alpn_not_supported()}, - {'dtlsv1.2', [], alpn_tests() -- [client_renegotiate]}, - {'dtlsv1', [], alpn_tests() -- [client_renegotiate]} + {'dtlsv1.2', [], alpn_tests() ++ alpn_npn_coexist()}, + {'dtlsv1', [], alpn_tests() ++ alpn_npn_coexist()} ]. alpn_tests() -> @@ -61,12 +64,16 @@ alpn_tests() -> client_alpn_and_server_no_support, client_no_support_and_server_alpn, client_alpn_npn_and_server_alpn, - client_alpn_npn_and_server_alpn_npn, - client_alpn_and_server_alpn_npn, client_renegotiate, session_reused ]. +alpn_npn_coexist() -> + [ + client_alpn_npn_and_server_alpn_npn, + client_alpn_and_server_alpn_npn + ]. + alpn_not_supported() -> [alpn_not_supported_client, alpn_not_supported_server @@ -77,8 +84,7 @@ init_per_suite(Config0) -> try crypto:start() of ok -> ssl_test_lib:clean_start(), - Config = ssl_test_lib:make_rsa_cert(Config0), - ssl_test_lib:cert_options(Config) + ssl_test_lib:make_rsa_cert(Config0) catch _:_ -> {skip, "Crypto did not start"} end. diff --git a/lib/ssl/test/ssl_api_SUITE.erl b/lib/ssl/test/ssl_api_SUITE.erl new file mode 100644 index 0000000000..fefecc0b65 --- /dev/null +++ b/lib/ssl/test/ssl_api_SUITE.erl @@ -0,0 +1,1976 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(ssl_api_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). +-include_lib("common_test/include/ct.hrl"). +-include_lib("ssl/src/ssl_api.hrl"). + +-define(SLEEP, 500). +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +all() -> + [ + {group, 'tlsv1.3'}, + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}, + {group, 'dtlsv1.2'}, + {group, 'dtlsv1'} + ]. + +groups() -> + [ + {'tlsv1.3', [], ((gen_api_tests() ++ tls13_group() ++ handshake_paus_tests()) -- [dh_params, honor_server_cipher_order, honor_client_cipher_order, + new_options_in_handshake]) + ++ (since_1_2() -- [conf_signature_algs])}, + {'tlsv1.2', [], gen_api_tests() ++ since_1_2() ++ handshake_paus_tests() ++ pre_1_3()}, + {'tlsv1.1', [], gen_api_tests() ++ handshake_paus_tests() ++ pre_1_3()}, + {'tlsv1', [], gen_api_tests() ++ handshake_paus_tests() ++ pre_1_3() ++ beast_mitigation_test()}, + {'sslv3', [], (gen_api_tests() -- [new_options_in_handshake]) ++ beast_mitigation_test() ++ pre_1_3()}, + {'dtlsv1.2', [], (gen_api_tests() -- [invalid_keyfile, invalid_certfile, invalid_cacertfile, + invalid_options, new_options_in_handshake]) ++ handshake_paus_tests() ++ pre_1_3()}, + {'dtlsv1', [], (gen_api_tests() -- [invalid_keyfile, invalid_certfile, invalid_cacertfile, + invalid_options, new_options_in_handshake]) ++ handshake_paus_tests() ++ pre_1_3()} + ]. + +since_1_2() -> + [ + conf_signature_algs, + no_common_signature_algs + ]. + +pre_1_3() -> + [ + default_reject_anonymous + ]. +gen_api_tests() -> + [ + peercert, + peercert_with_client_cert, + connection_information, + secret_connection_info, + versions, + active_n, + dh_params, + hibernate, + hibernate_right_away, + listen_socket, + recv_active, + recv_active_once, + recv_active_n, + recv_timeout, + recv_close, + controlling_process, + controller_dies, + controlling_process_transport_accept_socket, + close_with_timeout, + close_in_error_state, + call_in_error_state, + close_transport_accept, + abuse_transport_accept_socket, + honor_server_cipher_order, + honor_client_cipher_order, + ipv6, + der_input, + new_options_in_handshake, + max_handshake_size, + invalid_certfile, + invalid_cacertfile, + invalid_keyfile, + options_not_proplist, + invalid_options + ]. + +handshake_paus_tests() -> + [ + handshake_continue, + handshake_continue_timeout, + hello_client_cancel, + hello_server_cancel + ]. + +%% Only relevant for SSL 3.0 and TLS 1.1 +beast_mitigation_test() -> + [%% Original option + rizzo_disabled, + %% Same effect as disable + rizzo_zero_n, + %% Same as default + rizzo_one_n_minus_one + ]. + +tls13_group() -> + [ + supported_groups, + honor_server_cipher_order_tls13, + honor_client_cipher_order_tls13 + ]. + + +init_per_suite(Config0) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + ssl_test_lib:make_rsa_cert(Config0) + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:unload(ssl), + application:stop(crypto). + + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + [{client_type, erlang}, + {server_type, erlang} | ssl_test_lib:init_tls_version(GroupName, Config)]; + false -> + {skip, "Missing crypto support"} + end; + _ -> + ssl:start(), + Config + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +init_per_testcase(prf, Config) -> + ssl_test_lib:ct_log_supported_protocol_versions(Config), + ct:timetrap({seconds, 10}), + Version = ssl_test_lib:protocol_version(Config), + PRFS = [md5, sha, sha256, sha384, sha512], + %% All are the result of running tls_v1:prf(PrfAlgo, <<>>, <<>>, <<>>, 16) + %% with the specified PRF algorithm + ExpectedPrfResults = + [{md5, <<96,139,180,171,236,210,13,10,28,32,2,23,88,224,235,199>>}, + {sha, <<95,3,183,114,33,169,197,187,231,243,19,242,220,228,70,151>>}, + {sha256, <<166,249,145,171,43,95,158,232,6,60,17,90,183,180,0,155>>}, + {sha384, <<153,182,217,96,186,130,105,85,65,103,123,247,146,91,47,106>>}, + {sha512, <<145,8,98,38,243,96,42,94,163,33,53,49,241,4,127,28>>}, + %% TLS 1.0 and 1.1 PRF: + {md5sha, <<63,136,3,217,205,123,200,177,251,211,17,229,132,4,173,80>>}], + TestPlan = prf_create_plan([Version], PRFS, ExpectedPrfResults), + [{prf_test_plan, TestPlan} | Config]; +init_per_testcase(_TestCase, Config) -> + ssl_test_lib:ct_log_supported_protocol_versions(Config), + ct:timetrap({seconds, 10}), + Config. + +end_per_testcase(internal_active_n, _Config) -> + application:unset_env(ssl, internal_active_n); +end_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +peercert() -> + [{doc,"Test API function peercert/1"}]. +peercert(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, peercert_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, peercert_result, []}}, + {options, ClientOpts}]), + + CertFile = proplists:get_value(certfile, ServerOpts), + [{'Certificate', BinCert, _}]= ssl_test_lib:pem_to_der(CertFile), + + ServerMsg = {error, no_peercert}, + ClientMsg = {ok, BinCert}, + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +peercert_with_client_cert() -> + [{doc,"Test API function peercert/1"}]. +peercert_with_client_cert(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, peercert_result, []}}, + {options, [{verify, verify_peer} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, peercert_result, []}}, + {options, ClientOpts}]), + + ServerCertFile = proplists:get_value(certfile, ServerOpts), + [{'Certificate', ServerBinCert, _}]= ssl_test_lib:pem_to_der(ServerCertFile), + ClientCertFile = proplists:get_value(certfile, ClientOpts), + [{'Certificate', ClientBinCert, _}]= ssl_test_lib:pem_to_der(ClientCertFile), + + ServerMsg = {ok, ClientBinCert}, + ClientMsg = {ok, ServerBinCert}, + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +connection_information() -> + [{doc,"Test the API function ssl:connection_information/1"}]. +connection_information(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, connection_information_result, []}}, + {options, ServerOpts}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, connection_information_result, []}}, + {options, ClientOpts}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- + +secret_connection_info() -> + [{doc,"Test the API function ssl:connection_information/2"}]. +secret_connection_info(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, secret_connection_info_result, []}}, + {options, [{verify, verify_peer} | ServerOpts]}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, secret_connection_info_result, []}}, + {options, [{verify, verify_peer} |ClientOpts]}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, true, Client, true), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +prf() -> + [{doc,"Test that ssl:prf/5 uses the negotiated PRF."}]. +prf(Config) when is_list(Config) -> + TestPlan = proplists:get_value(prf_test_plan, Config), + case TestPlan of + [] -> ct:fail({error, empty_prf_test_plan}); + _ -> lists:foreach(fun(Suite) -> + lists:foreach( + fun(Test) -> + V = proplists:get_value(tls_ver, Test), + C = proplists:get_value(ciphers, Test), + E = proplists:get_value(expected, Test), + P = proplists:get_value(prf, Test), + prf_run_test(Config, V, C, E, P) + end, Suite) + end, TestPlan) + end. + +%%-------------------------------------------------------------------- +dh_params() -> + [{doc,"Test to specify DH-params file in server."}]. + +dh_params(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + DataDir = proplists:get_value(data_dir, Config), + DHParamFile = filename:join(DataDir, "dHParam.pem"), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{dhfile, DHParamFile} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, + [{ciphers,[{dhe_rsa,aes_256_cbc,sha}]} | + ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +conf_signature_algs() -> + [{doc,"Test to set the signature_algs option on both client and server"}]. +conf_signature_algs(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false}, {signature_algs, [{sha256, rsa}]} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false}, {signature_algs, [{sha256, rsa}]} | ClientOpts]}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- +no_common_signature_algs() -> + [{doc,"Set the signature_algs option so that there client and server does not share any hash sign algorithms"}]. +no_common_signature_algs(Config) when is_list(Config) -> + + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, [{signature_algs, [{sha256, rsa}]} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{signature_algs, [{sha384, rsa}]} + | ClientOpts]}]), + + ssl_test_lib:check_server_alert(Server, Client, insufficient_security). + +%%-------------------------------------------------------------------- +handshake_continue() -> + [{doc, "Test API function ssl:handshake_continue/3"}]. +handshake_continue(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ssl_test_lib:ssl_options([{reuseaddr, true}, + {verify, verify_peer}, + {handshake, hello} | ServerOpts + ], + Config)}, + {continue_options, proplists:delete(reuseaddr, ServerOpts)} + ]), + + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ssl_test_lib:ssl_options([{handshake, hello}, + {verify, verify_peer} | ClientOpts + ], + Config)}, + {continue_options, proplists:delete(reuseaddr, ClientOpts)}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%------------------------------------------------------------------ +handshake_continue_timeout() -> + [{doc, "Test API function ssl:handshake_continue/3 with short timeout"}]. +handshake_continue_timeout(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {timeout, 1}, + {options, ssl_test_lib:ssl_options([{reuseaddr, true}, {handshake, hello}, + {verify, verify_peer} | ServerOpts], + Config)}, + {continue_options, proplists:delete(reuseaddr, ServerOpts)} + ]), + + Port = ssl_test_lib:inet_port(Server), + + + ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, [{verify, verify_peer} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, {error,timeout}), + ssl_test_lib:close(Server). + + +%%-------------------------------------------------------------------- +hello_client_cancel() -> + [{doc, "Test API function ssl:handshake_cancel/1 on the client side"}]. +hello_client_cancel(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, ssl_test_lib:ssl_options([{handshake, hello}, + {verify, verify_peer} | ServerOpts], Config)}, + {continue_options, proplists:delete(reuseaddr, ServerOpts)}]), + + Port = ssl_test_lib:inet_port(Server), + + %% That is ssl:handshake_cancel returns ok + {connect_failed, ok} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, ssl_test_lib:ssl_options([{handshake, hello}, + {verify, verify_peer} | ClientOpts], Config)}, + {continue_options, cancel}]), + ssl_test_lib:check_server_alert(Server, user_canceled). +%%-------------------------------------------------------------------- +hello_server_cancel() -> + [{doc, "Test API function ssl:handshake_cancel/1 on the server side"}]. +hello_server_cancel(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, ssl_test_lib:ssl_options([{handshake, hello}, + {verify, verify_peer} | ServerOpts + ], Config)}, + {continue_options, cancel}]), + + Port = ssl_test_lib:inet_port(Server), + + ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, ssl_test_lib:ssl_options([{handshake, hello}, + {verify, verify_peer} | ClientOpts + ], Config)}, + {continue_options, proplists:delete(reuseaddr, ClientOpts)}]), + + ssl_test_lib:check_result(Server, ok). + +%%-------------------------------------------------------------------- +versions() -> + [{doc,"Test API function versions/0"}]. + +versions(Config) when is_list(Config) -> + [_|_] = Versions = ssl:versions(), + ct:log("~p~n", [Versions]). + +%%-------------------------------------------------------------------- +%% Test case adapted from gen_tcp_misc_SUITE. +active_n() -> + [{doc,"Test {active,N} option"}]. + +active_n(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + Port = ssl_test_lib:inet_port(node()), + N = 3, + LS = ok(ssl:listen(Port, [{active,N}|ServerOpts])), + [{active,N}] = ok(ssl:getopts(LS, [active])), + active_n_common(LS, N), + Self = self(), + spawn_link(fun() -> + S0 = ok(ssl:transport_accept(LS)), + {ok, S} = ssl:handshake(S0), + ok = ssl:setopts(S, [{active,N}]), + [{active,N}] = ok(ssl:getopts(S, [active])), + ssl:controlling_process(S, Self), + Self ! {server, S} + end), + C = ok(ssl:connect("localhost", Port, [{active,N}|ClientOpts])), + [{active,N}] = ok(ssl:getopts(C, [active])), + S = receive + {server, S0} -> S0 + after + 1000 -> + exit({error, connect}) + end, + active_n_common(C, N), + active_n_common(S, N), + ok = ssl:setopts(C, [{active,N}]), + ok = ssl:setopts(S, [{active,N}]), + ReceiveMsg = fun(Socket, Msg) -> + receive + {ssl,Socket,Msg} -> + ok; + {ssl,Socket,Begin} -> + receive + {ssl,Socket,End} -> + Msg = Begin ++ End, + ok + after 1000 -> + exit(timeout) + end + after 1000 -> + exit(timeout) + end + end, + repeat(3, fun(I) -> + Msg = "message "++integer_to_list(I), + ok = ssl:send(C, Msg), + ReceiveMsg(S, Msg), + ok = ssl:send(S, Msg), + ReceiveMsg(C, Msg) + end), + receive + {ssl_passive,S} -> + [{active,false}] = ok(ssl:getopts(S, [active])) + after + 1000 -> + exit({error,ssl_passive}) + end, + receive + {ssl_passive,C} -> + [{active,false}] = ok(ssl:getopts(C, [active])) + after + 1000 -> + exit({error,ssl_passive}) + end, + LS2 = ok(ssl:listen(0, [{active,0}])), + receive + {ssl_passive,LS2} -> + [{active,false}] = ok(ssl:getopts(LS2, [active])) + after + 1000 -> + exit({error,ssl_passive}) + end, + ok = ssl:close(LS2), + ok = ssl:close(C), + ok = ssl:close(S), + ok = ssl:close(LS), + ok. + +hibernate() -> + [{doc,"Check that an SSL connection that is started with option " + "{hibernate_after, 1000} indeed hibernates after 1000ms of " + "inactivity"}]. + +hibernate(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + {Client, #sslsocket{pid=[Pid|_]}} = ssl_test_lib:start_client([return_socket, + {node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{hibernate_after, 1000}|ClientOpts]}]), + {current_function, _} = + process_info(Pid, current_function), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ct:sleep(1500), + {current_function, {erlang, hibernate, 3}} = + process_info(Pid, current_function), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +hibernate_right_away() -> + [{doc,"Check that an SSL connection that is configured to hibernate " + "after 0 or 1 milliseconds hibernates as soon as possible and not " + "crashes"}]. + +hibernate_right_away(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + StartServerOpts = [{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}], + StartClientOpts = [return_socket, + {node, ClientNode}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}], + + Server1 = ssl_test_lib:start_server(StartServerOpts), + Port1 = ssl_test_lib:inet_port(Server1), + {Client1, #sslsocket{pid = [Pid1|_]}} = ssl_test_lib:start_client(StartClientOpts ++ + [{port, Port1}, {options, [{hibernate_after, 0}|ClientOpts]}]), + + ssl_test_lib:check_result(Server1, ok, Client1, ok), + + ct:sleep(1000), %% Schedule out + + {current_function, {erlang, hibernate, 3}} = + process_info(Pid1, current_function), + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1), + + Server2 = ssl_test_lib:start_server(StartServerOpts), + Port2 = ssl_test_lib:inet_port(Server2), + {Client2, #sslsocket{pid = [Pid2|_]}} = ssl_test_lib:start_client(StartClientOpts ++ + [{port, Port2}, {options, [{hibernate_after, 1}|ClientOpts]}]), + + ssl_test_lib:check_result(Server2, ok, Client2, ok), + + ct:sleep(1000), %% Schedule out + + {current_function, {erlang, hibernate, 3}} = + process_info(Pid2, current_function), + + ssl_test_lib:close(Server2), + ssl_test_lib:close(Client2). + +listen_socket() -> + [{doc,"Check error handling and inet compliance when calling API functions with listen sockets."}]. + +listen_socket(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ok, ListenSocket} = ssl:listen(0, ServerOpts), + + %% This can be a valid thing to do as + %% options are inherited by the accept socket + ok = ssl:controlling_process(ListenSocket, self()), + + {ok, _} = ssl:sockname(ListenSocket), + + {error, enotconn} = ssl:send(ListenSocket, <<"data">>), + {error, enotconn} = ssl:recv(ListenSocket, 0), + {error, enotconn} = ssl:connection_information(ListenSocket), + {error, enotconn} = ssl:peername(ListenSocket), + {error, enotconn} = ssl:peercert(ListenSocket), + {error, enotconn} = ssl:renegotiate(ListenSocket), + {error, enotconn} = ssl:prf(ListenSocket, 'master_secret', <<"Label">>, [client_random], 256), + {error, enotconn} = ssl:shutdown(ListenSocket, read_write), + + ok = ssl:close(ListenSocket). + +%%-------------------------------------------------------------------- +recv_active() -> + [{doc,"Test recv on active socket"}]. + +recv_active(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, try_recv_active, []}}, + {options, [{active, true} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, try_recv_active, []}}, + {options, [{active, true} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +recv_active_once() -> + [{doc,"Test recv on active (once) socket"}]. + +recv_active_once(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, try_recv_active_once, []}}, + {options, [{active, once} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, try_recv_active_once, []}}, + {options, [{active, once} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +recv_active_n() -> + [{doc,"Test recv on active (n) socket"}]. + +recv_active_n(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, try_recv_active_once, []}}, + {options, [{active, 1} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, try_recv_active_once, []}}, + {options, [{active, 1} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +recv_timeout() -> + [{doc,"Test ssl:ssl_accept timeout"}]. + +recv_timeout(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, send_recv_result_timeout_server, []}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + send_recv_result_timeout_client, []}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Client, ok, Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +recv_close() -> + [{doc,"Special case of call error handling"}]. +recv_close(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, do_recv_close, []}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + {_Client, #sslsocket{} = SslSocket} = ssl_test_lib:start_client([return_socket, + {node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + ssl:close(SslSocket), + ssl_test_lib:check_result(Server, ok). + + + +%%-------------------------------------------------------------------- +controlling_process() -> + [{doc,"Test API function controlling_process/2"}]. + +controlling_process(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + ClientMsg = "Server hello", + ServerMsg = "Client hello", + + Server = ssl_test_lib:start_server([ + {node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + controlling_process_result, [self(), + ServerMsg]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + {Client, CSocket} = ssl_test_lib:start_client([return_socket, + {node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + controlling_process_result, [self(), + ClientMsg]}}, + {options, ClientOpts}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ServerMsg = ssl_test_lib:active_recv(CSocket, length(ServerMsg)), + %% We do not have the TLS server socket but all messages form the client + %% socket are now read, so ramining are form the server socket + ClientMsg = ssl_active_recv(length(ClientMsg)), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +controller_dies() -> + [{doc,"Test that the socket is closed after controlling process dies"}]. +controller_dies(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + ClientMsg = "Hello server", + ServerMsg = "Hello client", + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + controller_dies_result, [self(), + ServerMsg]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + controller_dies_result, [self(), + ClientMsg]}}, + {options, ClientOpts}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), + ct:sleep(?SLEEP), %% so that they are connected + + process_flag(trap_exit, true), + + %% Test that clients die + exit(Client, killed), + get_close(Client, ?LINE), + + %% Test that clients die when process disappear + Server ! listen, + Tester = self(), + Connect = fun(Pid) -> + {ok, Socket} = ssl:connect(Hostname, Port, ClientOpts), + %% Make sure server finishes and verification + %% and is in coonection state before + %% killing client + ct:sleep(?SLEEP), + Pid ! {self(), connected, Socket}, + receive die_nice -> normal end + end, + Client2 = spawn_link(fun() -> Connect(Tester) end), + receive {Client2, connected, _Socket} -> Client2 ! die_nice end, + + get_close(Client2, ?LINE), + + %% Test that clients die when the controlling process have changed + Server ! listen, + + Client3 = spawn_link(fun() -> Connect(Tester) end), + Controller = spawn_link(fun() -> receive die_nice -> normal end end), + receive + {Client3, connected, Socket} -> + ok = ssl:controlling_process(Socket, Controller), + Client3 ! die_nice + end, + + ct:log("Wating on exit ~p~n",[Client3]), + receive {'EXIT', Client3, normal} -> ok end, + + receive %% Client3 is dead but that doesn't matter, socket should not be closed. + Unexpected -> + ct:log("Unexpected ~p~n",[Unexpected]), + ct:fail({line, ?LINE-1}) + after 1000 -> + ok + end, + Controller ! die_nice, + get_close(Controller, ?LINE), + + %% Test that servers die + Server ! listen, + LastClient = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + controller_dies_result, [self(), + ClientMsg]}}, + {options, ClientOpts}]), + ct:sleep(?SLEEP), %% so that they are connected + + exit(Server, killed), + get_close(Server, ?LINE), + process_flag(trap_exit, false), + ssl_test_lib:close(LastClient). +%%-------------------------------------------------------------------- +controlling_process_transport_accept_socket() -> + [{doc,"Only ssl:handshake and ssl:controlling_process is allowed for transport_accept:sockets"}]. +controlling_process_transport_accept_socket(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_transport_control([{node, ServerNode}, + {port, 0}, + {from, self()}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + _Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, ClientOpts}]), + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server). + +%%-------------------------------------------------------------------- +close_with_timeout() -> + [{doc,"Test normal (not downgrade) ssl:close/2"}]. +close_with_timeout(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, tls_close, []}}, + {options,[{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, tls_close, []}}, + {options, [{active, false} |ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok). + +%%-------------------------------------------------------------------- +close_in_error_state() -> + [{doc,"Special case of closing socket in error state"}]. +close_in_error_state(Config) when is_list(Config) -> + ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts = [{cacertfile, "foo.pem"} | proplists:delete(cacertfile, ServerOpts0)], + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + _ = spawn_link(?MODULE, run_error_server_close, [[self() | ServerOpts]]), + receive + {_Pid, Port} -> + spawn_link(?MODULE, run_client_error, [[Port, ClientOpts]]) + end, + receive + ok -> + ok; + Other -> + ct:fail(Other) + end. + +%%-------------------------------------------------------------------- +call_in_error_state() -> + [{doc,"Special case of call error handling"}]. +call_in_error_state(Config) when is_list(Config) -> + ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), + ServerOpts = [{cacertfile, "foo.pem"} | proplists:delete(cacertfile, ServerOpts0)], + Pid = spawn_link(?MODULE, run_error_server, [[self() | ServerOpts]]), + receive + {Pid, Port} -> + spawn_link(?MODULE, run_client_error, [[Port, ClientOpts]]) + end, + receive + {error, closed} -> + ok; + Other -> + ct:fail(Other) + end. +%%-------------------------------------------------------------------- +close_transport_accept() -> + [{doc,"Tests closing ssl socket when waiting on ssl:transport_accept/1"}]. + +close_transport_accept(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), + + Port = 0, + Opts = [{active, false} | ServerOpts], + {ok, ListenSocket} = rpc:call(ServerNode, ssl, listen, [Port, Opts]), + spawn_link(fun() -> + ct:sleep(?SLEEP), + rpc:call(ServerNode, ssl, close, [ListenSocket]) + end), + case rpc:call(ServerNode, ssl, transport_accept, [ListenSocket]) of + {error, closed} -> + ok; + Other -> + exit({?LINE, Other}) + end. +%%-------------------------------------------------------------------- +abuse_transport_accept_socket() -> + [{doc,"Only ssl:handshake and ssl:controlling_process is allowed for transport_accept:sockets"}]. +abuse_transport_accept_socket(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server_transport_abuse_socket([{node, ServerNode}, + {port, 0}, + {from, self()}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- + +invalid_keyfile() -> + [{doc,"Test what happens with an invalid key file"}]. +invalid_keyfile(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + BadKeyFile = filename:join([proplists:get_value(priv_dir, Config), + "badkey.pem"]), + BadOpts = [{keyfile, BadKeyFile}| proplists:delete(keyfile, ServerOpts)], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, BadOpts}]), + + Port = ssl_test_lib:inet_port(Server), + + Client = + ssl_test_lib:start_client_error([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {from, self()}, {options, ClientOpts}]), + + File = proplists:get_value(keyfile,BadOpts), + ssl_test_lib:check_result(Server, {error,{options, {keyfile, File, {error,enoent}}}}, Client, + {error, closed}). + +%%-------------------------------------------------------------------- +honor_server_cipher_order() -> + [{doc,"Test API honor server cipher order."}]. +honor_server_cipher_order(Config) when is_list(Config) -> + ClientCiphers = [#{key_exchange => dhe_rsa, + cipher => aes_128_cbc, + mac => sha, + prf => default_prf}, + #{key_exchange => dhe_rsa, + cipher => aes_256_cbc, + mac => sha, + prf => default_prf}], + ServerCiphers = [#{key_exchange => dhe_rsa, + cipher => aes_256_cbc, + mac =>sha, + prf => default_prf}, + #{key_exchange => dhe_rsa, + cipher => aes_128_cbc, + mac => sha, + prf => default_prf}], + honor_cipher_order(Config, true, ServerCiphers, ClientCiphers, #{key_exchange => dhe_rsa, + cipher => aes_256_cbc, + mac => sha, + prf => default_prf}). + +%%-------------------------------------------------------------------- +honor_client_cipher_order() -> + [{doc,"Test API honor server cipher order."}]. +honor_client_cipher_order(Config) when is_list(Config) -> + ClientCiphers = [#{key_exchange => dhe_rsa, + cipher => aes_128_cbc, + mac => sha, + prf => default_prf}, + #{key_exchange => dhe_rsa, + cipher => aes_256_cbc, + mac => sha, + prf => default_prf}], + ServerCiphers = [#{key_exchange => dhe_rsa, + cipher => aes_256_cbc, + mac =>sha, + prf => default_prf}, + #{key_exchange => dhe_rsa, + cipher => aes_128_cbc, + mac => sha, + prf => default_prf}], + honor_cipher_order(Config, false, ServerCiphers, ClientCiphers, #{key_exchange => dhe_rsa, + cipher => aes_128_cbc, + mac => sha, + prf => default_prf}). + +%%-------------------------------------------------------------------- +ipv6() -> + [{require, ipv6_hosts}, + {doc,"Test ipv6."}]. +ipv6(Config) when is_list(Config) -> + {ok, Hostname0} = inet:gethostname(), + + case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of + true -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = + ssl_test_lib:run_where(Config, ipv6), + Server = ssl_test_lib:start_server([{node, ServerNode}, + {port, 0}, {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, + [inet6, {active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, + [inet6, {active, false} | ClientOpts]}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client); + false -> + {skip, "Host does not support IPv6"} + end. + +%%-------------------------------------------------------------------- +der_input() -> + [{doc,"Test to input certs and key as der"}]. + +der_input(Config) when is_list(Config) -> + DataDir = proplists:get_value(data_dir, Config), + DHParamFile = filename:join(DataDir, "dHParam.pem"), + + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + [CADb | _] = element(6, State), + + Size = ets:info(CADb, size), + + SeverVerifyOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ServerCert, ServerKey, ServerCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} | + SeverVerifyOpts]), + ClientVerifyOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + {ClientCert, ClientKey, ClientCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} | + ClientVerifyOpts]), + ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}, + {dh, DHParams}, + {cert, ServerCert}, {key, ServerKey}, {cacerts, ServerCaCerts}], + ClientOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}, + {dh, DHParams}, + {cert, ClientCert}, {key, ClientKey}, {cacerts, ClientCaCerts}], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + Size = ets:info(CADb, size). + +%%-------------------------------------------------------------------- +invalid_certfile() -> + [{doc,"Test what happens with an invalid cert file"}]. + +invalid_certfile(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + BadCertFile = filename:join([proplists:get_value(priv_dir, Config), + "badcert.pem"]), + ServerBadOpts = [{certfile, BadCertFile}| proplists:delete(certfile, ServerOpts)], + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, ServerBadOpts}]), + + Port = ssl_test_lib:inet_port(Server), + + Client = + ssl_test_lib:start_client_error([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {from, self()}, + {options, ClientOpts}]), + File = proplists:get_value(certfile, ServerBadOpts), + ssl_test_lib:check_result(Server, {error,{options, {certfile, File, {error,enoent}}}}, + Client, {error, closed}). + + +%%-------------------------------------------------------------------- +invalid_cacertfile() -> + [{doc,"Test what happens with an invalid cacert file"}]. + +invalid_cacertfile(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + BadCACertFile = filename:join([proplists:get_value(priv_dir, Config), + "badcacert.pem"]), + ServerBadOpts = [{cacertfile, BadCACertFile}| proplists:delete(cacertfile, ServerOpts)], + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server0 = + ssl_test_lib:start_server_error([{node, ServerNode}, + {port, 0}, {from, self()}, + {options, ServerBadOpts}]), + + Port0 = ssl_test_lib:inet_port(Server0), + + + Client0 = + ssl_test_lib:start_client_error([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {from, self()}, + {options, ClientOpts}]), + + File0 = proplists:get_value(cacertfile, ServerBadOpts), + + ssl_test_lib:check_result(Server0, {error, {options, {cacertfile, File0,{error,enoent}}}}, + Client0, {error, closed}), + + File = File0 ++ "do_not_exit.pem", + ServerBadOpts1 = [{cacertfile, File}|proplists:delete(cacertfile, ServerBadOpts)], + + Server1 = + ssl_test_lib:start_server_error([{node, ServerNode}, + {port, 0}, {from, self()}, + {options, ServerBadOpts1}]), + + Port1 = ssl_test_lib:inet_port(Server1), + + Client1 = + ssl_test_lib:start_client_error([{node, ClientNode}, + {port, Port1}, {host, Hostname}, + {from, self()}, + {options, ClientOpts}]), + + + ssl_test_lib:check_result(Server1, {error, {options, {cacertfile, File,{error,enoent}}}}, + Client1, {error, closed}), + ok. + +%%-------------------------------------------------------------------- +new_options_in_handshake() -> + [{doc,"Test that you can set ssl options in handshake/3 and not only in tcp upgrade"}]. +new_options_in_handshake(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + Version = ssl_test_lib:protocol_version(Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + [_, Cipher | _] = ssl:filter_cipher_suites(ssl:cipher_suites(all, Version), + [{key_exchange, + fun(dhe_rsa) -> + true; + (ecdhe_rsa) -> + true; + (ecdh_rsa) -> + true; + (rsa) -> + true; + (_) -> + false + end + }]), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {ssl_extra_opts, [{versions, [Version]}, + {ciphers,[Cipher]}]}, %% To be set in ssl_accept/3 + {mfa, {?MODULE, connection_info_result, []}}, + {options, ServerOpts}]), + + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, connection_info_result, []}}, + {options, [{ciphers, [Cipher]} | ClientOpts]}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ServerMsg = ClientMsg = {ok, {Version, Cipher}}, + + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%------------------------------------------------------------------- +max_handshake_size() -> + [{doc,"Test that we can set max_handshake_size to max value."}]. + +max_handshake_size(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{max_handshake_size, 8388607} |ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{max_handshake_size, 8388607} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok). + + +%%------------------------------------------------------------------- +options_not_proplist() -> + [{doc,"Test what happens if an option is not a key value tuple"}]. + +options_not_proplist(Config) when is_list(Config) -> + BadOption = {client_preferred_next_protocols, + client, [<<"spdy/3">>,<<"http/1.1">>], <<"http/1.1">>}, + {option_not_a_key_value_tuple, BadOption} = + ssl:connect("twitter.com", 443, [binary, {active, false}, + BadOption]). + +%%------------------------------------------------------------------- +invalid_options() -> + [{doc,"Test what happens when we give invalid options"}]. + +invalid_options(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Check = fun(Client, Server, {versions, [sslv2, sslv3]} = Option) -> + ssl_test_lib:check_result(Server, + {error, {options, {sslv2, Option}}}, + Client, + {error, {options, {sslv2, Option}}}); + (Client, Server, Option) -> + ssl_test_lib:check_result(Server, + {error, {options, Option}}, + Client, + {error, {options, Option}}) + end, + + TestOpts = + [{versions, [sslv2, sslv3]}, + {verify, 4}, + {verify_fun, function}, + {fail_if_no_peer_cert, 0}, + {verify_client_once, 1}, + {depth, four}, + {certfile, 'cert.pem'}, + {keyfile,'key.pem' }, + {password, foo}, + {cacertfile, ""}, + {dhfile,'dh.pem' }, + {ciphers, [{foo, bar, sha, ignore}]}, + {reuse_session, foo}, + {reuse_sessions, 0}, + {renegotiate_at, "10"}, + {mode, depech}, + {packet, 8.0}, + {packet_size, "2"}, + {header, a}, + {active, trice}, + {key, 'key.pem' }], + + [begin + Server = + ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, [TestOpt | ServerOpts]}]), + %% Will never reach a point where port is used. + Client = + ssl_test_lib:start_client_error([{node, ClientNode}, {port, 0}, + {host, Hostname}, {from, self()}, + {options, [TestOpt | ClientOpts]}]), + Check(Client, Server, TestOpt), + ok + end || TestOpt <- TestOpts], + ok. +%%------------------------------------------------------------------- + +default_reject_anonymous()-> + [{doc,"Test that by default anonymous cipher suites are rejected "}]. +default_reject_anonymous(Config) when is_list(Config) -> + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + Version = ssl_test_lib:protocol_version(Config), + TLSVersion = ssl_test_lib:tls_version(Version), + + [CipherSuite | _] = ssl_test_lib:ecdh_dh_anonymous_suites(TLSVersion), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {options, + [{ciphers,[CipherSuite]} | + ClientOpts]}]), + + ssl_test_lib:check_server_alert(Server, Client, insufficient_security). + +%%------------------------------------------------------------------- +%% Note that these test only test that the options are valid to set. As application data +%% is a stream you can not test that the send acctually splits it up as when it arrives +%% again at the user layer it may be concatenated. But COVER can show that the split up +%% code has been run. + +rizzo_disabled() -> + [{doc, "Test original beast mitigation disable option for SSL 3.0 and TLS 1.0"}]. + +rizzo_disabled(Config) -> + ClientOpts = [{beast_mitigation, disabled} | ssl_test_lib:ssl_options(client_rsa_opts, Config)], + ServerOpts = [{beast_mitigation, disabled} | ssl_test_lib:ssl_options(server_rsa_opts, Config)], + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). +%%------------------------------------------------------------------- +rizzo_zero_n() -> + [{doc, "Test zero_n beast mitigation option (same affect as original disable option) for SSL 3.0 and TLS 1.0"}]. + +rizzo_zero_n(Config) -> + ClientOpts = [{beast_mitigation, zero_n} | ssl_test_lib:ssl_options(client_rsa_opts, Config)], + ServerOpts = [{beast_mitigation, zero_n} | ssl_test_lib:ssl_options(server_rsa_opts, Config)], + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). +%%------------------------------------------------------------------- +rizzo_one_n_minus_one () -> + [{doc, "Test beast_mitigation option one_n_minus_one (same affect as default) for SSL 3.0 and TLS 1.0"}]. + +rizzo_one_n_minus_one (Config) -> + ClientOpts = [{beast_mitigation, one_n_minus_one } | ssl_test_lib:ssl_options(client_rsa_opts, Config)], + ServerOpts = [{beast_mitigation, one_n_minus_one} | ssl_test_lib:ssl_options(server_rsa_opts, Config)], + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +supported_groups() -> + [{doc,"Test the supported_groups option in TLS 1.3."}]. + +supported_groups(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{supported_groups, [x448, x25519]} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{supported_groups,[x448]} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +honor_client_cipher_order_tls13() -> + [{doc,"Test API honor server cipher order in TLS 1.3."}]. +honor_client_cipher_order_tls13(Config) when is_list(Config) -> + ClientCiphers = [#{key_exchange => any, + cipher => aes_256_gcm, + mac => aead, + prf => sha384}, + #{key_exchange => any, + cipher => aes_128_gcm, + mac => aead, + prf => sha256}], + ServerCiphers = [#{key_exchange => any, + cipher => aes_128_gcm, + mac => aead, + prf => sha256}, + #{key_exchange => any, + cipher => aes_256_gcm, + mac => aead, + prf => sha384}], + honor_cipher_order(Config, false, ServerCiphers, ClientCiphers, #{key_exchange => any, + cipher => aes_256_gcm, + mac => aead, + prf => sha384}). + +%%-------------------------------------------------------------------- +honor_server_cipher_order_tls13() -> + [{doc,"Test API honor server cipher order in TLS 1.3."}]. +honor_server_cipher_order_tls13(Config) when is_list(Config) -> + ClientCiphers = [#{key_exchange => any, + cipher => aes_256_gcm, + mac => aead, + prf => sha384}, + #{key_exchange => any, + cipher => aes_128_gcm, + mac => aead, + prf => sha256}], + ServerCiphers = [#{key_exchange => any, + cipher => aes_128_gcm, + mac => aead, + prf => sha256}, + #{key_exchange => any, + cipher => aes_256_gcm, + mac => aead, + prf => sha384}], + honor_cipher_order(Config, true, ServerCiphers, ClientCiphers, #{key_exchange => any, + cipher => aes_128_gcm, + mac => aead, + prf => sha256}). + + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- + +peercert_result(Socket) -> + ssl:peercert(Socket). + +connection_information_result(Socket) -> + {ok, Info = [_ | _]} = ssl:connection_information(Socket), + case length(Info) > 3 of + true -> + %% Atleast one ssl_option() is set + ct:log("Info ~p", [Info]), + ok; + false -> + ct:fail(no_ssl_options_returned) + end. +secret_connection_info_result(Socket) -> + {ok, [{protocol, Protocol}]} = ssl:connection_information(Socket, [protocol]), + {ok, ConnInfo} = ssl:connection_information(Socket, [client_random, server_random, master_secret]), + check_connection_info(Protocol, ConnInfo). + + +%% In TLS 1.3 the master_secret field is used to store multiple secrets from the key schedule and it is a tuple. +%% client_random and server_random are not used in the TLS 1.3 key schedule. +check_connection_info('tlsv1.3', [{client_random, ClientRand}, {master_secret, {master_secret, MasterSecret}}]) -> + is_binary(ClientRand) andalso is_binary(MasterSecret); +check_connection_info('tlsv1.3', [{server_random, ServerRand}, {master_secret, {master_secret, MasterSecret}}]) -> + is_binary(ServerRand) andalso is_binary(MasterSecret); +check_connection_info(_, [{client_random, ClientRand}, {server_random, ServerRand}, {master_secret, MasterSecret}]) -> + is_binary(ClientRand) andalso is_binary(ServerRand) andalso is_binary(MasterSecret); +check_connection_info(_, _) -> + false. + + +prf_create_plan(TlsVersions, PRFs, Results) -> + lists:foldl(fun(Ver, Acc) -> + A = prf_ciphers_and_expected(Ver, PRFs, Results), + [A|Acc] + end, [], TlsVersions). + +prf_ciphers_and_expected(TlsVer, PRFs, Results) -> + case TlsVer of + TlsVer when TlsVer == sslv3 orelse TlsVer == tlsv1 + orelse TlsVer == 'tlsv1.1' orelse TlsVer == 'dtlsv1' -> + Ciphers = ssl:cipher_suites(), + {_, Expected} = lists:keyfind(md5sha, 1, Results), + [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected}, {prf, md5sha}]]; + TlsVer when TlsVer == 'tlsv1.2' orelse TlsVer == 'dtlsv1.2'-> + lists:foldl( + fun(PRF, Acc) -> + Ciphers = prf_get_ciphers(TlsVer, PRF), + case Ciphers of + [] -> + ct:log("No ciphers for PRF algorithm ~p. Skipping.", [PRF]), + Acc; + Ciphers -> + {_, Expected} = lists:keyfind(PRF, 1, Results), + [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected}, + {prf, PRF}] | Acc] + end + end, [], PRFs) + end. + +prf_get_ciphers(_, PRF) -> + lists:filter( + fun(C) when tuple_size(C) == 4 andalso + element(4, C) == PRF -> + true; + (_) -> + false + end, + ssl:cipher_suites()). + +prf_run_test(_, TlsVer, [], _, Prf) -> + ct:fail({error, cipher_list_empty, TlsVer, Prf}); +prf_run_test(Config, TlsVer, Ciphers, Expected, Prf) -> + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + BaseOpts = [{active, true}, {versions, [TlsVer]}, {ciphers, Ciphers}, {protocol, tls_or_dtls(TlsVer)}], + ServerOpts = BaseOpts ++ proplists:get_value(server_opts, Config), + ClientOpts = BaseOpts ++ proplists:get_value(client_opts, Config), + Server = ssl_test_lib:start_server( + [{node, ServerNode}, {port, 0}, {from, self()}, + {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client( + [{node, ClientNode}, {port, Port}, + {host, Hostname}, {from, self()}, + {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}}, + {options, ClientOpts}]), + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +prf_verify_value(Socket, TlsVer, Expected, Algo) -> + Ret = ssl:prf(Socket, <<>>, <<>>, [<<>>], 16), + case TlsVer of + sslv3 -> + case Ret of + {error, undefined} -> ok; + _ -> + {error, {expected, {error, undefined}, + got, Ret, tls_ver, TlsVer, prf_algorithm, Algo}} + end; + _ -> + case Ret of + {ok, Expected} -> ok; + {ok, Val} -> {error, {expected, Expected, got, Val, tls_ver, TlsVer, + prf_algorithm, Algo}} + end + end. + +tls_or_dtls('dtlsv1') -> + dtls; +tls_or_dtls('dtlsv1.2') -> + dtls; +tls_or_dtls(_) -> + tls. + +active_n_common(S, N) -> + ok = ssl:setopts(S, [{active,-N}]), + receive + {ssl_passive, S} -> ok + after + 1000 -> + error({error,ssl_passive_failure}) + end, + [{active,false}] = ok(ssl:getopts(S, [active])), + ok = ssl:setopts(S, [{active,0}]), + receive + {ssl_passive, S} -> ok + after + 1000 -> + error({error,ssl_passive_failure}) + end, + ok = ssl:setopts(S, [{active,32767}]), + {error,{options,_}} = ssl:setopts(S, [{active,1}]), + {error,{options,_}} = ssl:setopts(S, [{active,-32769}]), + ok = ssl:setopts(S, [{active,-32768}]), + receive + {ssl_passive, S} -> ok + after + 1000 -> + error({error,ssl_passive_failure}) + end, + [{active,false}] = ok(ssl:getopts(S, [active])), + ok = ssl:setopts(S, [{active,N}]), + ok = ssl:setopts(S, [{active,true}]), + [{active,true}] = ok(ssl:getopts(S, [active])), + receive + _ -> error({error,active_n}) + after + 0 -> + ok + end, + ok = ssl:setopts(S, [{active,N}]), + ok = ssl:setopts(S, [{active,once}]), + [{active,once}] = ok(ssl:getopts(S, [active])), + receive + _ -> error({error,active_n}) + after + 0 -> + ok + end, + {error,{options,_}} = ssl:setopts(S, [{active,32768}]), + ok = ssl:setopts(S, [{active,false}]), + [{active,false}] = ok(ssl:getopts(S, [active])), + ok. + +ok({ok,V}) -> V. + +repeat(N, Fun) -> + repeat(N, N, Fun). + +repeat(N, T, Fun) when is_integer(N), N > 0 -> + Fun(T-N), + repeat(N-1, T, Fun); +repeat(_, _, _) -> + ok. + +try_recv_active(Socket) -> + ssl:send(Socket, "Hello world"), + {error, einval} = ssl:recv(Socket, 11), + ok. +try_recv_active_once(Socket) -> + {error, einval} = ssl:recv(Socket, 11), + ok. + +controlling_process_result(Socket, Pid, Msg) -> + ok = ssl:controlling_process(Socket, Pid), + %% Make sure other side has evaluated controlling_process + %% before message is sent + ct:sleep(?SLEEP), + ssl:send(Socket, Msg), + no_result_msg. + +controller_dies_result(_Socket, _Pid, _Msg) -> + receive Result -> Result end. +get_close(Pid, Where) -> + receive + {'EXIT', Pid, _Reason} -> + receive + {_, {ssl_closed, Socket}} -> + ct:log("Socket closed ~p~n",[Socket]); + Unexpected -> + ct:log("Unexpected ~p~n",[Unexpected]), + ct:fail({line, ?LINE-1}) + after 5000 -> + ct:fail({timeout, {line, ?LINE, Where}}) + end; + Unexpected -> + ct:log("Unexpected ~p~n",[Unexpected]), + ct:fail({line, ?LINE-1}) + after 5000 -> + ct:fail({timeout, {line, ?LINE, Where}}) + end. + +ssl_active_recv(N) -> + ssl_active_recv(N, []). + +ssl_active_recv(0, Acc) -> + Acc; +ssl_active_recv(N, Acc) -> + receive + {ssl, _, Bytes} -> + ssl_active_recv(N-length(Bytes), Acc ++ Bytes) + end. + +send_recv_result_timeout_client(Socket) -> + {error, timeout} = ssl:recv(Socket, 11, 500), + {error, timeout} = ssl:recv(Socket, 11, 0), + ssl:send(Socket, "Hello world"), + receive + Msg -> + io:format("Msg ~p~n",[Msg]) + after 500 -> + ok + end, + {ok, "Hello world"} = ssl:recv(Socket, 11, 500), + ok. +send_recv_result_timeout_server(Socket) -> + ssl:send(Socket, "Hello"), + {ok, "Hello world"} = ssl:recv(Socket, 11), + ssl:send(Socket, " world"), + ok. + +do_recv_close(Socket) -> + {error, closed} = ssl:recv(Socket, 11), + receive + {_,{error,closed}} -> + error_extra_close_sent_to_user_process + after 500 -> + ok + end. + +tls_close(Socket) -> + ok = ssl_test_lib:send_recv_result(Socket), + case ssl:close(Socket, 5000) of + ok -> + ok; + {error, closed} -> + ok; + Other -> + ct:fail(Other) + end. + +run_error_server_close([Pid | Opts]) -> + {ok, Listen} = ssl:listen(0, Opts), + {ok,{_, Port}} = ssl:sockname(Listen), + Pid ! {self(), Port}, + {ok, Socket} = ssl:transport_accept(Listen), + Pid ! ssl:close(Socket). + +run_error_server([ Pid | Opts]) -> + {ok, Listen} = ssl:listen(0, Opts), + {ok,{_, Port}} = ssl:sockname(Listen), + Pid ! {self(), Port}, + {ok, Socket} = ssl:transport_accept(Listen), + Pid ! ssl:controlling_process(Socket, self()). + +run_client_error([Port, Opts]) -> + ssl:connect("localhost", Port, Opts). + +honor_cipher_order(Config, Honor, ServerCiphers, ClientCiphers, Expected) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, connection_info_result, []}}, + {options, [{ciphers, ServerCiphers}, {honor_cipher_order, Honor} + | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, connection_info_result, []}}, + {options, [{ciphers, ClientCiphers}, {honor_cipher_order, Honor} + | ClientOpts]}]), + + Version = ssl_test_lib:protocol_version(Config), + + ServerMsg = ClientMsg = {ok, {Version, Expected}}, + + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +connection_info_result(Socket) -> + {ok, Info} = ssl:connection_information(Socket, [protocol, selected_cipher_suite]), + {ok, {proplists:get_value(protocol, Info), proplists:get_value(selected_cipher_suite, Info)}}. + +der_input_opts(Opts) -> + Certfile = proplists:get_value(certfile, Opts), + CaCertsfile = proplists:get_value(cacertfile, Opts), + Keyfile = proplists:get_value(keyfile, Opts), + Dhfile = proplists:get_value(dhfile, Opts), + [{_, Cert, _}] = ssl_test_lib:pem_to_der(Certfile), + [{Asn1Type, Key, _}] = ssl_test_lib:pem_to_der(Keyfile), + [{_, DHParams, _}] = ssl_test_lib:pem_to_der(Dhfile), + CaCerts = + lists:map(fun(Entry) -> + {_, CaCert, _} = Entry, + CaCert + end, ssl_test_lib:pem_to_der(CaCertsfile)), + {Cert, {Asn1Type, Key}, CaCerts, DHParams}. diff --git a/lib/ssl/test/ssl_basic_SUITE_data/dHParam.pem b/lib/ssl/test/ssl_api_SUITE_data/dHParam.pem index feb581da30..feb581da30 100644 --- a/lib/ssl/test/ssl_basic_SUITE_data/dHParam.pem +++ b/lib/ssl/test/ssl_api_SUITE_data/dHParam.pem diff --git a/lib/ssl/test/ssl_app_env_SUITE.erl b/lib/ssl/test/ssl_app_env_SUITE.erl new file mode 100644 index 0000000000..27fbcb8e47 --- /dev/null +++ b/lib/ssl/test/ssl_app_env_SUITE.erl @@ -0,0 +1,171 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(ssl_app_env_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). +-include_lib("common_test/include/ct.hrl"). +-include_lib("ssl/src/ssl_api.hrl"). + +-define(SLEEP, 500). +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +all() -> + [ + {group, 'tlsv1.3'}, + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}, + {group, 'dtlsv1.2'}, + {group, 'dtlsv1'} + ]. + +groups() -> + [ + {'tlsv1.3', [], tests()}, + {'tlsv1.2', [], tests()}, + {'tlsv1.1', [], tests()}, + {'tlsv1', [], tests()}, + {'sslv3', [], tests()}, + {'dtlsv1.2', [], tests()}, + {'dtlsv1', [], tests()} + ]. + +tests() -> + [ + internal_active_1, + protocol_versions, + empty_protocol_versions + ]. + + +init_per_suite(Config0) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + ssl_test_lib:make_rsa_cert(Config0) + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:unload(ssl), + application:stop(crypto). + + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + [{client_type, erlang}, + {server_type, erlang} | ssl_test_lib:init_tls_version(GroupName, Config)]; + false -> + {skip, "Missing crypto support"} + end; + _ -> + ssl:start(), + Config + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +init_per_testcase(internal_active_1, Config) -> + ssl:stop(), + application:load(ssl), + application:set_env(ssl, internal_active_n, 1), + ssl:start(), + ct:timetrap({seconds, 5}), + Config; +init_per_testcase(protocol_versions, Config) -> + Version = ssl_test_lib:protocol_version(Config), + case atom_to_list(Version) of + "d" ++ _ -> + ssl:stop(), + application:load(ssl), + application:set_env(ssl, dtls_protocol_version, [Version]), + ssl:start(); + _ -> + ssl:stop(), + application:load(ssl), + application:set_env(ssl, protocol_version, [Version]), + ssl:start() + end, + ct:timetrap({seconds, 5}), + Config; +init_per_testcase(empty_protocol_versions, Config) -> + ssl:stop(), + application:load(ssl), + ssl_test_lib:clean_env(), + application:set_env(ssl, protocol_version, []), + application:set_env(ssl, dtls_protocol_version, []), + ssl:start(), + ct:timetrap({seconds, 5}), + Config; +init_per_testcase(_TestCase, Config) -> + ct:timetrap({seconds, 5}), + Config. + +end_per_testcase(_, _Config) -> + ssl_test_lib:clean_start(). + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +%%-------------------------------------------------------------------- +internal_active_1() -> + [{doc,"Test internal active 1 (behave as internal active once)"}]. + +internal_active_1(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +%%-------------------------------------------------------------------- +protocol_versions() -> + [{doc,"Test to set a list of protocol versions in app environment."}]. + +protocol_versions(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +%%-------------------------------------------------------------------- +empty_protocol_versions() -> + [{doc,"Test to set an empty list of protocol versions in app environment."}]. + +empty_protocol_versions(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index f5ce3a2b65..355cd31070 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -26,15 +26,8 @@ -compile(export_all). -include_lib("common_test/include/ct.hrl"). --include_lib("public_key/include/public_key.hrl"). +-include_lib("ssl/src/ssl_api.hrl"). --include("ssl_api.hrl"). --include("ssl_cipher.hrl"). --include("ssl_internal.hrl"). --include("ssl_alert.hrl"). --include("ssl_internal.hrl"). --include("tls_record.hrl"). --include("tls_handshake.hrl"). -define(TIMEOUT, 20000). -define(EXPIRE, 10). @@ -49,222 +42,35 @@ all() -> [ {group, basic}, - {group, basic_tls}, - {group, options}, - {group, options_tls}, - {group, session}, - {group, 'dtlsv1.2'}, - {group, 'dtlsv1'}, - {group, 'tlsv1.3'}, - {group, 'tlsv1.2'}, - {group, 'tlsv1.1'}, - {group, 'tlsv1'}, - {group, 'sslv3'} + {group, options} ]. groups() -> [{basic, [], basic_tests()}, - {basic_tls, [], basic_tests_tls()}, - {options, [], options_tests()}, - {options_tls, [], options_tests_tls()}, - {'dtlsv1.2', [], all_versions_groups()}, - {'dtlsv1', [], all_versions_groups()}, - {'tlsv1.3', [], tls13_test_group()}, - {'tlsv1.2', [], all_versions_groups() ++ tls_versions_groups() ++ [conf_signature_algs, no_common_signature_algs]}, - {'tlsv1.1', [], all_versions_groups() ++ tls_versions_groups()}, - {'tlsv1', [], all_versions_groups() ++ tls_versions_groups() ++ rizzo_tests()}, - {'sslv3', [], all_versions_groups() ++ tls_versions_groups() ++ rizzo_tests() ++ [tls_ciphersuite_vs_version]}, - {api,[], api_tests()}, - {api_tls,[], api_tests_tls()}, - {session, [], session_tests()}, - {renegotiate, [], renegotiate_tests()}, - {ciphers, [], cipher_tests()}, - {error_handling_tests, [], error_handling_tests()}, - {error_handling_tests_tls, [], error_handling_tests_tls()} + {options, [], options_tests()} ]. -tls_versions_groups ()-> - [ - {group, api_tls}, - {group, error_handling_tests_tls}]. - -all_versions_groups ()-> - [{group, api}, - {group, renegotiate}, - {group, ciphers}, - {group, error_handling_tests}]. - - basic_tests() -> [app, - appup, - alerts, - alert_details, - alert_details_not_too_big, + appup, version_option, connect_twice, connect_dist, - clear_pem_cache, defaults, fallback, cipher_format, - suite_to_str - ]. - -basic_tests_tls() -> - [tls_send_close - ]. - -options_tests() -> - [der_input, - ssl_options_not_proplist, - raw_ssl_option, - invalid_inet_get_option, - invalid_inet_get_option_not_list, - invalid_inet_get_option_improper_list, - invalid_inet_set_option, - invalid_inet_set_option_not_list, - invalid_inet_set_option_improper_list, - dh_params, - invalid_certfile, - invalid_cacertfile, - invalid_keyfile, - invalid_options, - protocol_versions, - empty_protocol_versions, - ipv6, - reuseaddr, - honor_server_cipher_order, - honor_client_cipher_order, - unordered_protocol_versions_server, - unordered_protocol_versions_client, - max_handshake_size -]. - -options_tests_tls() -> - [tls_misc_ssl_options, - tls_tcp_reuseaddr]. - -api_tests() -> - [secret_connection_info, - connection_information, - peercert, - peercert_with_client_cert, - versions, + tls_versions_option, eccs, - controlling_process, - getstat, - close_with_timeout, - hibernate, - hibernate_right_away, - listen_socket, - ssl_recv_timeout, - server_name_indication_option, - accept_pool, - prf, - socket_options, - active_n, - internal_active_1, cipher_suites, - handshake_continue, - handshake_continue_timeout, - hello_client_cancel, - hello_server_cancel - ]. - -api_tests_tls() -> - [tls_versions_option, - tls_upgrade, - tls_upgrade_with_timeout, - tls_ssl_accept_timeout, - tls_downgrade, - tls_shutdown, - tls_shutdown_write, - tls_shutdown_both, - tls_shutdown_error, - peername, - sockname, - tls_socket_options, - new_options_in_accept - ]. - -session_tests() -> - [reuse_session, - reuse_session_expired, - server_does_not_want_to_reuse_session, - no_reuses_session_server_restart_new_cert, - no_reuses_session_server_restart_new_cert_file]. - -renegotiate_tests() -> - [client_renegotiate, - server_renegotiate, - client_secure_renegotiate, - client_secure_renegotiate_fallback, - client_renegotiate_reused_session, - server_renegotiate_reused_session, - client_no_wrap_sequence_number, - server_no_wrap_sequence_number, - renegotiate_dos_mitigate_active, - renegotiate_dos_mitigate_passive, - renegotiate_dos_mitigate_absolute]. - -cipher_tests() -> - [old_cipher_suites, - cipher_suites_mix, - default_reject_anonymous]. - -error_handling_tests()-> - [close_transport_accept, - recv_active, - recv_active_once, - recv_active_n, - recv_error_handling, - call_in_error_state, - close_in_error_state, - abuse_transport_accept_socket, - controlling_process_transport_accept_socket - ]. - -error_handling_tests_tls()-> - [controller_dies, - tls_client_closes_socket, - tls_closed_in_active_once, - tls_tcp_error_propagation_in_active_mode, - tls_tcp_connect, - tls_tcp_connect_big, - tls_dont_crash_on_handshake_garbage + old_cipher_suites, + cipher_suites_mix ]. -rizzo_tests() -> - [rizzo, - no_rizzo_rc4, - rizzo_one_n_minus_one, - rizzo_zero_n, - rizzo_disabled]. - -%% For testing TLS 1.3 features and possible regressions -tls13_test_group() -> - [handshake_continue_tls13_client, - tls13_enable_client_side, - tls13_enable_server_side, - tls_record_1_3_encode_decode, - tls13_finished_verify_data, - tls13_1_RTT_handshake, - tls12_ssl_server_tls13_ssl_client, - tls13_basic_ssl_server_openssl_client, - tls13_custom_groups_ssl_server_openssl_client, - tls13_hello_retry_request_ssl_server_openssl_client, - tls13_client_auth_empty_cert_alert_ssl_server_openssl_client, - tls13_client_auth_empty_cert_ssl_server_openssl_client, - tls13_client_auth_ssl_server_openssl_client, - tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client, - tls13_hrr_client_auth_empty_cert_ssl_server_openssl_client, - tls13_hrr_client_auth_ssl_server_openssl_client, - tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client, - tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client, - tls13_connection_information]. +options_tests() -> + [ + unordered_protocol_versions_server, + unordered_protocol_versions_client]. -%%-------------------------------------------------------------------- init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of @@ -287,222 +93,6 @@ end_per_suite(_Config) -> application:stop(crypto). %%-------------------------------------------------------------------- - -init_per_group(GroupName, Config) when GroupName == basic_tls; - GroupName == options_tls; - GroupName == options; - GroupName == basic; - GroupName == session; - GroupName == error_handling_tests_tls; - GroupName == tls13_test_group - -> - ssl_test_lib:clean_tls_version(Config); -init_per_group(GroupName, Config) -> - ssl_test_lib:clean_tls_version(Config), - case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of - true -> - ssl_test_lib:init_tls_version(GroupName, Config); - _ -> - case ssl_test_lib:sufficient_crypto_support(GroupName) of - true -> - ssl:start(), - Config; - false -> - {skip, "Missing crypto support"} - end - end. - -end_per_group(GroupName, Config) -> - case ssl_test_lib:is_tls_version(GroupName) of - true -> - ssl_test_lib:clean_tls_version(Config); - false -> - Config - end. - -%%-------------------------------------------------------------------- -init_per_testcase(Case, Config) when Case == unordered_protocol_versions_client; - Case == unordered_protocol_versions_server-> - case proplists:get_value(supported, ssl:versions()) of - ['tlsv1.2' | _] -> - ct:timetrap({seconds, 5}), - Config; - _ -> - {skip, "TLS 1.2 need but not supported on this platform"} - end; - -init_per_testcase(protocol_versions, Config) -> - ssl:stop(), - application:load(ssl), - %% For backwards compatibility sslv2 should be filtered out. - application:set_env(ssl, protocol_version, [sslv2, sslv3, tlsv1]), - ssl:start(), - ct:timetrap({seconds, 5}), - Config; - -init_per_testcase(reuse_session_expired, Config) -> - ssl:stop(), - application:load(ssl), - ssl_test_lib:clean_env(), - application:set_env(ssl, session_lifetime, ?EXPIRE), - application:set_env(ssl, session_delay_cleanup_time, 500), - ssl:start(), - ct:timetrap({seconds, 30}), - Config; - -init_per_testcase(empty_protocol_versions, Config) -> - ssl:stop(), - application:load(ssl), - ssl_test_lib:clean_env(), - application:set_env(ssl, protocol_version, []), - ssl:start(), - ct:timetrap({seconds, 5}), - Config; - -init_per_testcase(fallback, Config) -> - case tls_record:highest_protocol_version([]) of - {3, N} when N > 1 -> - ct:timetrap({seconds, 5}), - Config; - _ -> - {skip, "Not relevant if highest supported version is less than 3.2"} - end; - -init_per_testcase(TestCase, Config) when TestCase == client_renegotiate; - TestCase == server_renegotiate; - TestCase == client_secure_renegotiate; - TestCase == client_renegotiate_reused_session; - TestCase == server_renegotiate_reused_session; - TestCase == client_no_wrap_sequence_number; - TestCase == server_no_wrap_sequence_number; - TestCase == renegotiate_dos_mitigate_active; - TestCase == renegotiate_dos_mitigate_passive; - TestCase == renegotiate_dos_mitigate_absolute -> - ssl_test_lib:ct_log_supported_protocol_versions(Config), - ct:timetrap({seconds, ?SEC_RENEGOTIATION_TIMEOUT + 5}), - Config; - -init_per_testcase(TestCase, Config) when TestCase == versions_option; - TestCase == tls_tcp_connect_big -> - ssl_test_lib:ct_log_supported_protocol_versions(Config), - ct:timetrap({seconds, 60}), - Config; - -init_per_testcase(version_option, Config) -> - ssl_test_lib:ct_log_supported_protocol_versions(Config), - ct:timetrap({seconds, 10}), - Config; - -init_per_testcase(reuse_session, Config) -> - ssl_test_lib:ct_log_supported_protocol_versions(Config), - ct:timetrap({seconds, 10}), - Config; - -init_per_testcase(rizzo, Config) -> - ssl_test_lib:ct_log_supported_protocol_versions(Config), - ct:timetrap({seconds, 60}), - Config; - -init_per_testcase(no_rizzo_rc4, Config) -> - ssl_test_lib:ct_log_supported_protocol_versions(Config), - ct:timetrap({seconds, 60}), - Config; - -init_per_testcase(rizzo_one_n_minus_one, Config) -> - ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]), - ct:timetrap({seconds, 60}), - rizzo_add_mitigation_option(one_n_minus_one, Config); - -init_per_testcase(rizzo_zero_n, Config) -> - ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]), - ct:timetrap({seconds, 60}), - rizzo_add_mitigation_option(zero_n, Config); - -init_per_testcase(rizzo_disabled, Config) -> - ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]), - ct:timetrap({seconds, 60}), - rizzo_add_mitigation_option(disabled, Config); - -init_per_testcase(TestCase, Config) when TestCase == no_reuses_session_server_restart_new_cert_file; - TestCase == no_reuses_session_server_restart_new_cert -> - ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]), - ct:timetrap({seconds, 15}), - Config; - -init_per_testcase(prf, Config) -> - ct:log("TLS/SSL version ~p~n ", [tls_record:supported_protocol_versions()]), - ct:timetrap({seconds, 40}), - case proplists:get_value(tc_group_path, Config) of - [] -> Prop = []; - [Prop] -> Prop - end, - case proplists:get_value(name, Prop) of - undefined -> TlsVersions = [sslv3, tlsv1, 'tlsv1.1', 'tlsv1.2']; - TlsVersion when is_atom(TlsVersion) -> - TlsVersions = [TlsVersion] - end, - PRFS=[md5, sha, sha256, sha384, sha512], - %All are the result of running tls_v1:prf(PrfAlgo, <<>>, <<>>, <<>>, 16) - %with the specified PRF algorithm - ExpectedPrfResults= - [{md5, <<96,139,180,171,236,210,13,10,28,32,2,23,88,224,235,199>>}, - {sha, <<95,3,183,114,33,169,197,187,231,243,19,242,220,228,70,151>>}, - {sha256, <<166,249,145,171,43,95,158,232,6,60,17,90,183,180,0,155>>}, - {sha384, <<153,182,217,96,186,130,105,85,65,103,123,247,146,91,47,106>>}, - {sha512, <<145,8,98,38,243,96,42,94,163,33,53,49,241,4,127,28>>}, - %TLS 1.0 and 1.1 PRF: - {md5sha, <<63,136,3,217,205,123,200,177,251,211,17,229,132,4,173,80>>}], - TestPlan = prf_create_plan(TlsVersions, PRFS, ExpectedPrfResults), - [{prf_test_plan, TestPlan} | Config]; - -init_per_testcase(TestCase, Config) when TestCase == tls_ssl_accept_timeout; - TestCase == tls_client_closes_socket; - TestCase == tls_closed_in_active_once; - TestCase == tls_downgrade -> - ssl:stop(), - ssl:start(), - ssl_test_lib:ct_log_supported_protocol_versions(Config), - ct:timetrap({seconds, 15}), - Config; -init_per_testcase(TestCase, Config) when TestCase == clear_pem_cache; - TestCase == der_input; - TestCase == defaults -> - ssl_test_lib:ct_log_supported_protocol_versions(Config), - %% White box test need clean start - ssl:stop(), - ssl:start(), - ct:timetrap({seconds, 20}), - Config; -init_per_testcase(raw_ssl_option, Config) -> - ct:timetrap({seconds, 5}), - case os:type() of - {unix,linux} -> - Config; - _ -> - {skip, "Raw options are platform-specific"} - end; - -init_per_testcase(accept_pool, Config) -> - ct:timetrap({seconds, 5}), - case proplists:get_value(protocol, Config) of - dtls -> - {skip, "Not yet supported on DTLS sockets"}; - _ -> - ssl_test_lib:ct_log_supported_protocol_versions(Config), - Config - end; - -init_per_testcase(internal_active_1, Config) -> - ssl:stop(), - application:load(ssl), - application:set_env(ssl, internal_active_n, 1), - ssl:start(), - ct:timetrap({seconds, 5}), - Config; - -init_per_testcase(controller_dies, Config) -> - ct:timetrap({seconds, 10}), - Config; init_per_testcase(eccs, Config) -> case ssl:eccs() of [] -> @@ -517,23 +107,8 @@ init_per_testcase(_TestCase, Config) -> ct:timetrap({seconds, 5}), Config. -end_per_testcase(reuse_session_expired, Config) -> - application:unset_env(ssl, session_lifetime), - application:unset_env(ssl, session_delay_cleanup_time), - end_per_testcase(default_action, Config); - -end_per_testcase(internal_active_n, Config) -> - application:unset_env(ssl, internal_active_n), - end_per_testcase(default_action, Config); - -end_per_testcase(Case, Config) when Case == protocol_versions; - Case == empty_protocol_versions-> - application:unset_env(ssl, protocol_versions), - end_per_testcase(default_action, Config); - end_per_testcase(_TestCase, Config) -> Config. - %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- @@ -547,669 +122,76 @@ appup() -> appup(Config) when is_list(Config) -> ok = ?t:appup_test(ssl). %%-------------------------------------------------------------------- -alerts() -> - [{doc, "Test ssl_alert:alert_txt/1"}]. -alerts(Config) when is_list(Config) -> - Descriptions = [?CLOSE_NOTIFY, ?UNEXPECTED_MESSAGE, ?BAD_RECORD_MAC, - ?DECRYPTION_FAILED_RESERVED, ?RECORD_OVERFLOW, ?DECOMPRESSION_FAILURE, - ?HANDSHAKE_FAILURE, ?BAD_CERTIFICATE, ?UNSUPPORTED_CERTIFICATE, - ?CERTIFICATE_REVOKED,?CERTIFICATE_EXPIRED, ?CERTIFICATE_UNKNOWN, - ?ILLEGAL_PARAMETER, ?UNKNOWN_CA, ?ACCESS_DENIED, ?DECODE_ERROR, - ?DECRYPT_ERROR, ?EXPORT_RESTRICTION, ?PROTOCOL_VERSION, - ?INSUFFICIENT_SECURITY, ?INTERNAL_ERROR, ?USER_CANCELED, - ?NO_RENEGOTIATION, ?UNSUPPORTED_EXTENSION, ?CERTIFICATE_UNOBTAINABLE, - ?UNRECOGNISED_NAME, ?BAD_CERTIFICATE_STATUS_RESPONSE, - ?BAD_CERTIFICATE_HASH_VALUE, ?UNKNOWN_PSK_IDENTITY, - 255 %% Unsupported/unknow alert will result in a description too - ], - Alerts = [?ALERT_REC(?WARNING, ?CLOSE_NOTIFY) | - [?ALERT_REC(?FATAL, Desc) || Desc <- Descriptions]], - lists:foreach(fun(Alert) -> - try ssl_alert:alert_txt(Alert) - catch - C:E:T -> - ct:fail({unexpected, {C, E, T}}) - end - end, Alerts). -%%-------------------------------------------------------------------- -alert_details() -> - [{doc, "Test that ssl_alert:alert_txt/1 result contains extendend error description"}]. -alert_details(Config) when is_list(Config) -> - Unique = make_ref(), - UniqueStr = lists:flatten(io_lib:format("~w", [Unique])), - Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY, Unique), - case string:str(ssl_alert:alert_txt(Alert), UniqueStr) of - 0 -> - ct:fail(error_details_missing); - _ -> - ok - end. - -%%-------------------------------------------------------------------- -alert_details_not_too_big() -> - [{doc, "Test that ssl_alert:alert_txt/1 limits printed depth of extended error description"}]. -alert_details_not_too_big(Config) when is_list(Config) -> - Reason = lists:duplicate(10, lists:duplicate(10, lists:duplicate(10, {some, data}))), - Alert = ?ALERT_REC(?WARNING, ?CLOSE_NOTIFY, Reason), - case length(ssl_alert:alert_txt(Alert)) < 1000 of - true -> - ok; - false -> - ct:fail(ssl_alert_text_too_big) - end. - -%%-------------------------------------------------------------------- -new_options_in_accept() -> - [{doc,"Test that you can set ssl options in ssl_accept/3 and not only in tcp upgrade"}]. -new_options_in_accept(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts0 = ssl_test_lib:ssl_options(server_dsa_opts, Config), - [_ , _ | ServerSslOpts] = ssl_test_lib:ssl_options(server_opts, Config), %% Remove non ssl opts - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Version = ssl_test_lib:protocol_options(Config, [{tls, sslv3}, {dtls, dtlsv1}]), - Cipher = ssl_test_lib:protocol_options(Config, [{tls, #{key_exchange =>rsa, - cipher => rc4_128, - mac => sha, - prf => default_prf - }}, - {dtls, #{key_exchange =>rsa, - cipher => aes_128_cbc, - mac => sha, - prf => default_prf - }}]), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {ssl_extra_opts, [{versions, [Version]}, - {ciphers,[Cipher]} | ServerSslOpts]}, %% To be set in ssl_accept/3 - {mfa, {?MODULE, connection_info_result, []}}, - {options, proplists:delete(cacertfile, ServerOpts0)}]), - - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, connection_info_result, []}}, - {options, [{versions, [Version]}, - {ciphers,[Cipher]} | ClientOpts]}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ServerMsg = ClientMsg = {ok, {Version, Cipher}}, +version_option() -> + [{doc, "Use version option and do no specify ciphers list. Bug specified incorrect ciphers"}]. +version_option(Config) when is_list(Config) -> + Versions = proplists:get_value(supported, ssl:versions()), + [version_option_test(Config, Version) || Version <- Versions]. - ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -handshake_continue() -> - [{doc, "Test API function ssl:handshake_continue/3"}]. -handshake_continue(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ssl_test_lib:ssl_options([{reuseaddr, true}, {handshake, hello}], - Config)}, - {continue_options, proplists:delete(reuseaddr, ServerOpts)} - ]), - - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ssl_test_lib:ssl_options([{handshake, hello}], - Config)}, - {continue_options, proplists:delete(reuseaddr, ClientOpts)}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - - -%%-------------------------------------------------------------------- -handshake_continue_tls13_client() -> - [{doc, "Test API function ssl:handshake_continue/3"}]. -handshake_continue_tls13_client(Config) when is_list(Config) -> - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], - - ClientOptsHello0 = ssl_test_lib:ssl_options([{handshake, hello}], Config), - ClientOptsHello = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOptsHello0], - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ssl_test_lib:ssl_options([{reuseaddr, true}, {handshake, hello}], - Config)}, - {continue_options, proplists:delete(reuseaddr, ServerOpts)} - ]), - - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ClientOptsHello}, - {continue_options, proplists:delete(reuseaddr, ClientOpts)}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - - -%%------------------------------------------------------------------ -handshake_continue_timeout() -> - [{doc, "Test API function ssl:handshake_continue/3 with short timeout"}]. -handshake_continue_timeout(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {timeout, 1}, - {options, ssl_test_lib:ssl_options([{reuseaddr, true}, {handshake, hello}], - Config)}, - {continue_options, proplists:delete(reuseaddr, ServerOpts)} - ]), - - Port = ssl_test_lib:inet_port(Server), - - - {connect_failed, _} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, ClientOpts}]), - - ssl_test_lib:check_result(Server, {error,timeout}), - ssl_test_lib:close(Server). - - -%%-------------------------------------------------------------------- -hello_client_cancel() -> - [{doc, "Test API function ssl:handshake_cancel/1 on the client side"}]. -hello_client_cancel(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, ssl_test_lib:ssl_options([{handshake, hello}], Config)}, - {continue_options, proplists:delete(reuseaddr, ServerOpts)}]), - - Port = ssl_test_lib:inet_port(Server), - - %% That is ssl:handshake_cancel returns ok - {connect_failed, ok} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, ssl_test_lib:ssl_options([{handshake, hello}], Config)}, - {continue_options, cancel}]), - ssl_test_lib:check_server_alert(Server, user_canceled). -%%-------------------------------------------------------------------- -hello_server_cancel() -> - [{doc, "Test API function ssl:handshake_cancel/1 on the server side"}]. -hello_server_cancel(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, ssl_test_lib:ssl_options([{handshake, hello}], Config)}, - {continue_options, cancel}]), - - Port = ssl_test_lib:inet_port(Server), - - {connect_failed, _} = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, ssl_test_lib:ssl_options([{handshake, hello}], Config)}, - {continue_options, proplists:delete(reuseaddr, ClientOpts)}]), - - ssl_test_lib:check_result(Server, ok). - -%%-------------------------------------------------------------------- -prf() -> - [{doc,"Test that ssl:prf/5 uses the negotiated PRF."}]. -prf(Config) when is_list(Config) -> - TestPlan = proplists:get_value(prf_test_plan, Config), - case TestPlan of - [] -> ct:fail({error, empty_prf_test_plan}); - _ -> lists:foreach(fun(Suite) -> - lists:foreach( - fun(Test) -> - V = proplists:get_value(tls_ver, Test), - C = proplists:get_value(ciphers, Test), - E = proplists:get_value(expected, Test), - P = proplists:get_value(prf, Test), - prf_run_test(Config, V, C, E, P) - end, Suite) - end, TestPlan) - end. - %%-------------------------------------------------------------------- - -secret_connection_info() -> - [{doc,"Test the API function ssl:connection_information/2"}]. -secret_connection_info(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, secret_connection_info_result, []}}, - {options, ServerOpts}]), - - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, secret_connection_info_result, []}}, - {options, ClientOpts}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, true, Client, true), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - - -%%-------------------------------------------------------------------- - -connection_information() -> - [{doc,"Test the API function ssl:connection_information/1"}]. -connection_information(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, connection_information_result, []}}, - {options, ServerOpts}]), - - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, connection_information_result, []}}, - {options, ClientOpts}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ServerMsg = ClientMsg = ok, - - ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - - -%%-------------------------------------------------------------------- -protocol_versions() -> - [{doc,"Test to set a list of protocol versions in app environment."}]. - -protocol_versions(Config) when is_list(Config) -> - basic_test(Config). - -%%-------------------------------------------------------------------- -empty_protocol_versions() -> - [{doc,"Test to set an empty list of protocol versions in app environment."}]. - -empty_protocol_versions(Config) when is_list(Config) -> - basic_test(Config). - -%%-------------------------------------------------------------------- - -controlling_process() -> - [{doc,"Test API function controlling_process/2"}]. - -controlling_process(Config) when is_list(Config) -> +connect_twice() -> + [{doc,""}]. +connect_twice(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - ClientMsg = "Server hello", - ServerMsg = "Client hello", - - Server = ssl_test_lib:start_server([ - {node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - controlling_process_result, [self(), - ServerMsg]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - {Client, CSocket} = ssl_test_lib:start_client([return_socket, - {node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - controlling_process_result, [self(), - ClientMsg]}}, - {options, ClientOpts}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ServerMsg = ssl_test_lib:active_recv(CSocket, length(ServerMsg)), - %% We do not have the TLS server socket but all messages form the client - %% socket are now read, so ramining are form the server socket - ClientMsg = ssl_active_recv(length(ClientMsg)), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -getstat() -> - [{doc,"Test API function getstat/2"}]. - -getstat(Config) when is_list(Config) -> - ClientOpts = ?config(client_opts, Config), - ServerOpts = ?config(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server1 = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false} | ServerOpts]}]), - Port1 = ssl_test_lib:inet_port(Server1), - Server2 = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false} | ServerOpts]}]), - Port2 = ssl_test_lib:inet_port(Server2), - {ok, ActiveC} = rpc:call(ClientNode, ssl, connect, - [Hostname,Port1,[{active, once}|ClientOpts]]), - {ok, PassiveC} = rpc:call(ClientNode, ssl, connect, - [Hostname,Port2,[{active, false}|ClientOpts]]), - - ct:log("Testcase ~p, Client ~p Servers ~p, ~p ~n", - [self(), self(), Server1, Server2]), - - %% We only check that the values are non-zero initially - %% (due to the handshake), and that sending more changes the values. - - %% Passive socket. - - {ok, InitialStats} = ssl:getstat(PassiveC), - ct:pal("InitialStats ~p~n", [InitialStats]), - [true] = lists:usort([0 =/= proplists:get_value(Name, InitialStats) - || Name <- [recv_cnt, recv_oct, recv_avg, recv_max, send_cnt, send_oct, send_avg, send_max]]), - - ok = ssl:send(PassiveC, "Hello world"), - wait_for_send(PassiveC), - {ok, SStats} = ssl:getstat(PassiveC, [send_cnt, send_oct]), - ct:pal("SStats ~p~n", [SStats]), - [true] = lists:usort([proplists:get_value(Name, SStats) =/= proplists:get_value(Name, InitialStats) - || Name <- [send_cnt, send_oct]]), - - %% Active socket. - {ok, InitialAStats} = ssl:getstat(ActiveC), - ct:pal("InitialAStats ~p~n", [InitialAStats]), - [true] = lists:usort([0 =/= proplists:get_value(Name, InitialAStats) - || Name <- [recv_cnt, recv_oct, recv_avg, recv_max, send_cnt, send_oct, send_avg, send_max]]), - - _ = receive - {ssl, ActiveC, _} -> - ok - after - ?SLEEP -> - exit(timeout) - end, - - ok = ssl:send(ActiveC, "Hello world"), - wait_for_send(ActiveC), - {ok, ASStats} = ssl:getstat(ActiveC, [send_cnt, send_oct]), - ct:pal("ASStats ~p~n", [ASStats]), - [true] = lists:usort([proplists:get_value(Name, ASStats) =/= proplists:get_value(Name, InitialAStats) - || Name <- [send_cnt, send_oct]]), - - ok. - -%%-------------------------------------------------------------------- -controller_dies() -> - [{doc,"Test that the socket is closed after controlling process dies"}]. -controller_dies(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - ClientMsg = "Hello server", - ServerMsg = "Hello client", - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - controller_dies_result, [self(), - ServerMsg]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - controller_dies_result, [self(), - ClientMsg]}}, - {options, ClientOpts}]), - ct:log("Testcase ~p, Client ~p Server ~p ~n", [self(), Client, Server]), - ct:sleep(?SLEEP), %% so that they are connected - - process_flag(trap_exit, true), - - %% Test that clients die - exit(Client, killed), - get_close(Client, ?LINE), - - %% Test that clients die when process disappear - Server ! listen, - Tester = self(), - Connect = fun(Pid) -> - {ok, Socket} = ssl:connect(Hostname, Port, ClientOpts), - %% Make sure server finishes and verification - %% and is in coonection state before - %% killing client - ct:sleep(?SLEEP), - Pid ! {self(), connected, Socket}, - receive die_nice -> normal end - end, - Client2 = spawn_link(fun() -> Connect(Tester) end), - receive {Client2, connected, _Socket} -> Client2 ! die_nice end, - - get_close(Client2, ?LINE), - - %% Test that clients die when the controlling process have changed - Server ! listen, - - Client3 = spawn_link(fun() -> Connect(Tester) end), - Controller = spawn_link(fun() -> receive die_nice -> normal end end), - receive - {Client3, connected, Socket} -> - ok = ssl:controlling_process(Socket, Controller), - Client3 ! die_nice - end, - - ct:log("Wating on exit ~p~n",[Client3]), - receive {'EXIT', Client3, normal} -> ok end, - - receive %% Client3 is dead but that doesn't matter, socket should not be closed. - Unexpected -> - ct:log("Unexpected ~p~n",[Unexpected]), - ct:fail({line, ?LINE-1}) - after 1000 -> - ok - end, - Controller ! die_nice, - get_close(Controller, ?LINE), - - %% Test that servers die - Server ! listen, - LastClient = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - controller_dies_result, [self(), - ClientMsg]}}, - {options, ClientOpts}]), - ct:sleep(?SLEEP), %% so that they are connected - - exit(Server, killed), - get_close(Server, ?LINE), - process_flag(trap_exit, false), - ssl_test_lib:close(LastClient). - -%%-------------------------------------------------------------------- -tls_client_closes_socket() -> - [{doc,"Test what happens when client closes socket before handshake is compleated"}]. - -tls_client_closes_socket(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - TcpOpts = [binary, {reuseaddr, true}], - - Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {tcp_options, TcpOpts}, - {ssl_options, ServerOpts}]), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{keepalive, true},{active, false} + | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{keepalive, true},{active, false} + | ClientOpts]}]), + Server ! listen, - Connect = fun() -> - {ok, _Socket} = rpc:call(ClientNode, gen_tcp, connect, - [Hostname, Port, [binary]]), - %% Make sure that ssl_accept is called before - %% client process ends and closes socket. - ct:sleep(?SLEEP) - end, - - _Client = spawn_link(Connect), - - ssl_test_lib:check_result(Server, {error,closed}). - -%%-------------------------------------------------------------------- -tls_closed_in_active_once() -> - [{doc, "Test that ssl_closed is delivered in active once with non-empty buffer, check ERL-420."}]. - -tls_closed_in_active_once(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {_ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), - TcpOpts = [binary, {reuseaddr, true}], - Port = ssl_test_lib:inet_port(node()), - Server = fun() -> - {ok, Listen} = gen_tcp:listen(Port, TcpOpts), - {ok, TcpServerSocket} = gen_tcp:accept(Listen), - {ok, ServerSocket} = ssl:ssl_accept(TcpServerSocket, ServerOpts), - lists:foreach( - fun(_) -> - ssl:send(ServerSocket, "some random message\r\n") - end, lists:seq(1, 20)), - %% Close TCP instead of SSL socket to trigger the bug: - gen_tcp:close(TcpServerSocket), - gen_tcp:close(Listen) - end, - spawn_link(Server), - {ok, Socket} = ssl:connect(Hostname, Port, [{active, false} | ClientOpts]), - Result = tls_closed_in_active_once_loop(Socket), - ssl:close(Socket), - case Result of - ok -> ok; - _ -> ct:fail(Result) - end. - -tls_closed_in_active_once_loop(Socket) -> - case ssl:setopts(Socket, [{active, once}]) of - ok -> - receive - {ssl, Socket, _} -> - tls_closed_in_active_once_loop(Socket); - {ssl_closed, Socket} -> - ok - after 5000 -> - no_ssl_closed_received - end; - {error, closed} -> - ok - end. -%%-------------------------------------------------------------------- -connect_dist() -> - [{doc,"Test a simple connect as is used by distribution"}]. - -connect_dist(Config) when is_list(Config) -> - ClientOpts0 = ssl_test_lib:ssl_options(client_kc_opts, Config), - ClientOpts = [{ssl_imp, new},{active, false}, {packet,4}|ClientOpts0], - ServerOpts0 = ssl_test_lib:ssl_options(server_kc_opts, Config), - ServerOpts = [{ssl_imp, new},{active, false}, {packet,4}|ServerOpts0], + {Client1, #sslsocket{}} = + ssl_test_lib:start_client([return_socket, + {node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{keepalive, true},{active, false} + | ClientOpts]}]), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, connect_dist_s, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, connect_dist_c, []}}, - {options, ClientOpts}]), - ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:check_result(Server, ok, Client1, ok), ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -clear_pem_cache() -> - [{doc,"Test that internal reference tabel is cleaned properly even when " - " the PEM cache is cleared" }]. -clear_pem_cache(Config) when is_list(Config) -> - {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), - [_, _,_, _, Prop] = StatusInfo, - State = ssl_test_lib:state(Prop), - [_,{FilRefDb, _} |_] = element(6, State), - {Server, Client} = basic_verify_test_no_close(Config), - CountReferencedFiles = fun({_, -1}, Acc) -> - Acc; - ({_, N}, Acc) -> - N + Acc - end, - - 2 = ets:foldl(CountReferencedFiles, 0, FilRefDb), - ssl:clear_pem_cache(), - _ = sys:get_status(whereis(ssl_manager)), - {Server1, Client1} = basic_verify_test_no_close(Config), - 4 = ets:foldl(CountReferencedFiles, 0, FilRefDb), - ssl_test_lib:close(Server), ssl_test_lib:close(Client), - ct:sleep(2000), - _ = sys:get_status(whereis(ssl_manager)), - 2 = ets:foldl(CountReferencedFiles, 0, FilRefDb), - ssl_test_lib:close(Server1), - ssl_test_lib:close(Client1), - ct:sleep(2000), - _ = sys:get_status(whereis(ssl_manager)), - 0 = ets:foldl(CountReferencedFiles, 0, FilRefDb). + ssl_test_lib:close(Client1). +defaults(Config) when is_list(Config)-> + Versions = ssl:versions(), + true = lists:member(sslv3, proplists:get_value(available, Versions)), + false = lists:member(sslv3, proplists:get_value(supported, Versions)), + true = lists:member('tlsv1', proplists:get_value(available, Versions)), + false = lists:member('tlsv1', proplists:get_value(supported, Versions)), + true = lists:member('tlsv1.1', proplists:get_value(available, Versions)), + false = lists:member('tlsv1.1', proplists:get_value(supported, Versions)), + true = lists:member('tlsv1.2', proplists:get_value(available, Versions)), + true = lists:member('tlsv1.2', proplists:get_value(supported, Versions)), + false = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites()), + true = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites(all)), + false = lists:member({rsa,des_cbc,sha}, ssl:cipher_suites()), + true = lists:member({rsa,des_cbc,sha}, ssl:cipher_suites(all)), + false = lists:member({dhe_rsa,des_cbc,sha}, ssl:cipher_suites()), + true = lists:member({dhe_rsa,des_cbc,sha}, ssl:cipher_suites(all)), + true = lists:member('dtlsv1.2', proplists:get_value(available_dtls, Versions)), + true = lists:member('dtlsv1', proplists:get_value(available_dtls, Versions)), + true = lists:member('dtlsv1.2', proplists:get_value(supported_dtls, Versions)), + false = lists:member('dtlsv1', proplists:get_value(supported_dtls, Versions)). -%%-------------------------------------------------------------------- fallback() -> [{doc, "Test TLS_FALLBACK_SCSV downgrade prevention"}]. @@ -1235,7 +217,6 @@ fallback(Config) when is_list(Config) -> | ClientOpts]}]), ssl_test_lib:check_server_alert(Server, Client, inappropriate_fallback). - %%-------------------------------------------------------------------- cipher_format() -> [{doc, "Test that cipher conversion from maps | tuples | stings to binarys works"}]. @@ -1249,175 +230,6 @@ cipher_format(Config) when is_list(Config) -> ssl:close(Socket2). %%-------------------------------------------------------------------- -suite_to_str() -> - [{doc, "Test that the suite_to_str API works"}]. -suite_to_str(Config) when is_list(Config) -> - "TLS_EMPTY_RENEGOTIATION_INFO_SCSV" = - ssl:suite_to_str(#{key_exchange => null, - cipher => null, - mac => null, - prf => null}), - "TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256" = - ssl:suite_to_str(#{key_exchange => ecdhe_ecdsa, - cipher => aes_128_gcm, - mac => aead, - prf => sha256}), - "TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256" = - ssl:suite_to_str(#{key_exchange => ecdh_rsa, - cipher => aes_128_cbc, - mac => sha256, - prf => sha256}). - -%%-------------------------------------------------------------------- - -peername() -> - [{doc,"Test API function peername/1"}]. - -peername(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, peername_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, peername_result, []}}, - {options, [{port, 0} | ClientOpts]}]), - - ClientPort = ssl_test_lib:inet_port(Client), - ServerIp = ssl_test_lib:node_to_hostip(ServerNode), - ClientIp = ssl_test_lib:node_to_hostip(ClientNode), - ServerMsg = {ok, {ClientIp, ClientPort}}, - ClientMsg = {ok, {ServerIp, Port}}, - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -peercert() -> - [{doc,"Test API function peercert/1"}]. -peercert(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, peercert_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, peercert_result, []}}, - {options, ClientOpts}]), - - CertFile = proplists:get_value(certfile, ServerOpts), - [{'Certificate', BinCert, _}]= ssl_test_lib:pem_to_der(CertFile), - - ServerMsg = {error, no_peercert}, - ClientMsg = {ok, BinCert}, - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -peercert_result(Socket) -> - ssl:peercert(Socket). -%%-------------------------------------------------------------------- - -peercert_with_client_cert() -> - [{doc,"Test API function peercert/1"}]. -peercert_with_client_cert(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_dsa_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_dsa_verify_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ClientNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, peercert_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ServerNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, peercert_result, []}}, - {options, ClientOpts}]), - - ServerCertFile = proplists:get_value(certfile, ServerOpts), - [{'Certificate', ServerBinCert, _}]= ssl_test_lib:pem_to_der(ServerCertFile), - ClientCertFile = proplists:get_value(certfile, ClientOpts), - [{'Certificate', ClientBinCert, _}]= ssl_test_lib:pem_to_der(ClientCertFile), - - ServerMsg = {ok, ClientBinCert}, - ClientMsg = {ok, ServerBinCert}, - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -sockname() -> - [{doc,"Test API function sockname/1"}]. -sockname(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, sockname_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, sockname_result, []}}, - {options, [{port, 0} | ClientOpts]}]), - - ClientPort = ssl_test_lib:inet_port(Client), - ServerIp = - case proplists:get_value(protocol, Config) of - dtls -> - %% DTLS sockets are not connected on the server side, - %% so we can only get a ClientIP, ServerIP will always be 0.0.0.0 - {0,0,0,0}; - _ -> - ssl_test_lib:node_to_hostip(ServerNode) - end, - - ClientIp = ssl_test_lib:node_to_hostip(ClientNode), - ServerMsg = {ok, {ServerIp, Port}}, - ClientMsg = {ok, {ClientIp, ClientPort}}, - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -sockname_result(S) -> - ssl:sockname(S). - -%%-------------------------------------------------------------------- cipher_suites() -> [{doc,"Test API function cipher_suites/2, filter_cipher_suites/2" @@ -1514,2750 +326,95 @@ cipher_suites_mix(Config) when is_list(Config) -> ssl_test_lib:check_result(Server, ok, Client, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). -%%-------------------------------------------------------------------- -tls_socket_options() -> - [{doc,"Test API function getopts/2 and setopts/2"}]. +unordered_protocol_versions_server() -> + [{doc,"Test that the highest protocol is selected even" + " when it is not first in the versions list."}]. -tls_socket_options(Config) when is_list(Config) -> +unordered_protocol_versions_server(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Values = [{mode, list}, {packet, 0}, {header, 0}, - {active, true}], - %% Shall be the reverse order of Values! - Options = [active, header, packet, mode], - - NewValues = [{mode, binary}, {active, once}], - %% Shall be the reverse order of NewValues! - NewOptions = [active, mode], - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, tls_socket_options_result, - [Options, Values, NewOptions, NewValues]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, tls_socket_options_result, - [Options, Values, NewOptions, NewValues]}}, - {options, ClientOpts}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - - {ok, Listen} = ssl:listen(0, ServerOpts), - {ok,[{mode,list}]} = ssl:getopts(Listen, [mode]), - ok = ssl:setopts(Listen, [{mode, binary}]), - {ok,[{mode, binary}]} = ssl:getopts(Listen, [mode]), - {ok,[{recbuf, _}]} = ssl:getopts(Listen, [recbuf]), - ssl:close(Listen). - -tls_socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) -> - %% Test get/set emulated opts - {ok, DefaultValues} = ssl:getopts(Socket, Options), - ssl:setopts(Socket, NewValues), - {ok, NewValues} = ssl:getopts(Socket, NewOptions), - %% Test get/set inet opts - {ok,[{nodelay,false}]} = ssl:getopts(Socket, [nodelay]), - ssl:setopts(Socket, [{nodelay, true}]), - {ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]), - {ok, All} = ssl:getopts(Socket, []), - ct:log("All opts ~p~n", [All]), - ok. - - -%%-------------------------------------------------------------------- -socket_options() -> - [{doc,"Test API function getopts/2 and setopts/2"}]. + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), -socket_options(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Values = [{mode, list}, {active, true}], - %% Shall be the reverse order of Values! - Options = [active, mode], - - NewValues = [{mode, binary}, {active, once}], - %% Shall be the reverse order of NewValues! - NewOptions = [active, mode], - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, socket_options_result, - [Options, Values, NewOptions, NewValues]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, socket_options_result, - [Options, Values, NewOptions, NewValues]}}, - {options, ClientOpts}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - - {ok, Listen} = ssl:listen(0, ServerOpts), - {ok,[{mode,list}]} = ssl:getopts(Listen, [mode]), - ok = ssl:setopts(Listen, [{mode, binary}]), - {ok,[{mode, binary}]} = ssl:getopts(Listen, [mode]), - {ok,[{recbuf, _}]} = ssl:getopts(Listen, [recbuf]), - ssl:close(Listen). - - -socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) -> - %% Test get/set emulated opts - {ok, DefaultValues} = ssl:getopts(Socket, Options), - ssl:setopts(Socket, NewValues), - {ok, NewValues} = ssl:getopts(Socket, NewOptions), - %% Test get/set inet opts - {ok,[{reuseaddr, _}]} = ssl:getopts(Socket, [reuseaddr]), - {ok, All} = ssl:getopts(Socket, []), - ct:log("All opts ~p~n", [All]), - ok. - - -%%-------------------------------------------------------------------- -invalid_inet_get_option() -> - [{doc,"Test handling of invalid inet options in getopts"}]. - -invalid_inet_get_option(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, get_invalid_inet_option, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ClientOpts}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -invalid_inet_get_option_not_list() -> - [{doc,"Test handling of invalid type in getopts"}]. - -invalid_inet_get_option_not_list(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, get_invalid_inet_option_not_list, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ClientOpts}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - - -get_invalid_inet_option_not_list(Socket) -> - {error, {options, {socket_options, some_invalid_atom_here}}} - = ssl:getopts(Socket, some_invalid_atom_here), - ok. - -%%-------------------------------------------------------------------- -invalid_inet_get_option_improper_list() -> - [{doc,"Test handling of invalid type in getopts"}]. - -invalid_inet_get_option_improper_list(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, get_invalid_inet_option_improper_list, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ClientOpts}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - - -get_invalid_inet_option_improper_list(Socket) -> - {error, {options, {socket_options, foo,_}}} = ssl:getopts(Socket, [packet | foo]), - ok. - -%%-------------------------------------------------------------------- -invalid_inet_set_option() -> - [{doc,"Test handling of invalid inet options in setopts"}]. - -invalid_inet_set_option(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, set_invalid_inet_option, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ClientOpts}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -set_invalid_inet_option(Socket) -> - {error, {options, {socket_options, {packet, foo}}}} = ssl:setopts(Socket, [{packet, foo}]), - {error, {options, {socket_options, {header, foo}}}} = ssl:setopts(Socket, [{header, foo}]), - {error, {options, {socket_options, {active, foo}}}} = ssl:setopts(Socket, [{active, foo}]), - {error, {options, {socket_options, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]), - ok. -%%-------------------------------------------------------------------- -invalid_inet_set_option_not_list() -> - [{doc,"Test handling of invalid type in setopts"}]. - -invalid_inet_set_option_not_list(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, set_invalid_inet_option_not_list, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ClientOpts}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - - -set_invalid_inet_option_not_list(Socket) -> - {error, {options, {not_a_proplist, some_invalid_atom_here}}} - = ssl:setopts(Socket, some_invalid_atom_here), - ok. - -%%-------------------------------------------------------------------- -invalid_inet_set_option_improper_list() -> - [{doc,"Test handling of invalid tye in setopts"}]. - -invalid_inet_set_option_improper_list(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, set_invalid_inet_option_improper_list, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ClientOpts}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -set_invalid_inet_option_improper_list(Socket) -> - {error, {options, {not_a_proplist, [{packet, 0} | {foo, 2}]}}} = - ssl:setopts(Socket, [{packet, 0} | {foo, 2}]), - ok. - -%%-------------------------------------------------------------------- -tls_misc_ssl_options() -> - [{doc,"Test what happens when we give valid options"}]. - -tls_misc_ssl_options(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - %% Check that ssl options not tested elsewhere are filtered away e.i. not passed to inet. - TestOpts = [{depth, 1}, - {key, undefined}, - {password, []}, - {reuse_session, fun(_,_,_,_) -> true end}, - {cb_info, {gen_tcp, tcp, tcp_closed, tcp_error}}], - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, TestOpts ++ ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, TestOpts ++ ClientOpts}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -ssl_options_not_proplist() -> - [{doc,"Test what happens if an option is not a key value tuple"}]. - -ssl_options_not_proplist(Config) when is_list(Config) -> - BadOption = {client_preferred_next_protocols, - client, [<<"spdy/3">>,<<"http/1.1">>], <<"http/1.1">>}, - {option_not_a_key_value_tuple, BadOption} = - ssl:connect("twitter.com", 443, [binary, {active, false}, - BadOption]). - -%%-------------------------------------------------------------------- -raw_ssl_option() -> - [{doc,"Ensure that a single 'raw' option is passed to ssl:listen correctly."}]. - -raw_ssl_option(Config) when is_list(Config) -> - % 'raw' option values are platform-specific; these are the Linux values: - IpProtoTcp = 6, - % Use TCP_KEEPIDLE, because (e.g.) TCP_MAXSEG can't be read back reliably. - TcpKeepIdle = 4, - KeepAliveTimeSecs = 55, - LOptions = [{raw, IpProtoTcp, TcpKeepIdle, <<KeepAliveTimeSecs:32/native>>}], - {ok, LSocket} = ssl:listen(0, LOptions), - % Per http://www.erlang.org/doc/man/inet.html#getopts-2, we have to specify - % exactly which raw option we want, and the size of the buffer. - {ok, [{raw, IpProtoTcp, TcpKeepIdle, <<KeepAliveTimeSecs:32/native>>}]} = ssl:getopts(LSocket, [{raw, IpProtoTcp, TcpKeepIdle, 4}]). - - -%%-------------------------------------------------------------------- -versions() -> - [{doc,"Test API function versions/0"}]. - -versions(Config) when is_list(Config) -> - [_|_] = Versions = ssl:versions(), - ct:log("~p~n", [Versions]). - - -%%-------------------------------------------------------------------- -eccs() -> - [{doc, "Test API functions eccs/0 and eccs/1"}]. - -eccs(Config) when is_list(Config) -> - [_|_] = All = ssl:eccs(), - [] = SSL3 = ssl:eccs(sslv3), - [_|_] = Tls = ssl:eccs(tlsv1), - [_|_] = Tls1 = ssl:eccs('tlsv1.1'), - [_|_] = Tls2 = ssl:eccs('tlsv1.2'), - [_|_] = Tls1 = ssl:eccs('dtlsv1'), - [_|_] = Tls2 = ssl:eccs('dtlsv1.2'), - %% ordering is currently unverified by the test - true = lists:sort(All) =:= lists:usort(SSL3 ++ Tls ++ Tls1 ++ Tls2), - ok. - -%%-------------------------------------------------------------------- -send_recv() -> - [{doc,""}]. -send_recv(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false} | ClientOpts]}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -tls_send_close() -> - [{doc,""}]. -tls_send_close(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - {ok, TcpS} = rpc:call(ClientNode, gen_tcp, connect, - [Hostname,Port,[binary, {active, false}]]), - {ok, SslS} = rpc:call(ClientNode, ssl, connect, - [TcpS,[{active, false}|ClientOpts]]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), self(), Server]), - ok = ssl:send(SslS, "Hello world"), - {ok,<<"Hello world">>} = ssl:recv(SslS, 11), - gen_tcp:close(TcpS), - {error, _} = ssl:send(SslS, "Hello world"). - -%%-------------------------------------------------------------------- -version_option() -> - [{doc, "Use version option and do no specify ciphers list. Bug specified incorrect ciphers"}]. -version_option(Config) when is_list(Config) -> - Versions = proplists:get_value(supported, ssl:versions()), - [version_option_test(Config, Version) || Version <- Versions]. - -%%-------------------------------------------------------------------- -close_transport_accept() -> - [{doc,"Tests closing ssl socket when waiting on ssl:transport_accept/1"}]. - -close_transport_accept(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), - - Port = 0, - Opts = [{active, false} | ServerOpts], - {ok, ListenSocket} = rpc:call(ServerNode, ssl, listen, [Port, Opts]), - spawn_link(fun() -> - ct:sleep(?SLEEP), - rpc:call(ServerNode, ssl, close, [ListenSocket]) - end), - case rpc:call(ServerNode, ssl, transport_accept, [ListenSocket]) of - {error, closed} -> - ok; - Other -> - exit({?LINE, Other}) - end. -%%-------------------------------------------------------------------- -recv_active() -> - [{doc,"Test recv on active socket"}]. - -recv_active(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, try_recv_active, []}}, - {options, [{active, true} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, try_recv_active, []}}, - {options, [{active, true} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -recv_active_once() -> - [{doc,"Test recv on active (once) socket"}]. - -recv_active_once(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, try_recv_active_once, []}}, - {options, [{active, once} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, try_recv_active_once, []}}, - {options, [{active, once} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - - - - - -%%-------------------------------------------------------------------- -recv_active_n() -> - [{doc,"Test recv on active (n) socket"}]. - -recv_active_n(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, try_recv_active_once, []}}, - {options, [{active, 1} | ServerOpts]}]), + {mfa, {?MODULE, protocol_info_result, []}}, + {options, [{versions, ['tlsv1.1', 'tlsv1.2']} | ServerOpts]}]), Port = ssl_test_lib:inet_port(Server), - Client = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, try_recv_active_once, []}}, - {options, [{active, 1} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - - -%%-------------------------------------------------------------------- -%% Test case adapted from gen_tcp_misc_SUITE. -active_n() -> - [{doc,"Test {active,N} option"}]. - -active_n(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - Port = ssl_test_lib:inet_port(node()), - N = 3, - LS = ok(ssl:listen(Port, [{active,N}|ServerOpts])), - [{active,N}] = ok(ssl:getopts(LS, [active])), - active_n_common(LS, N), - Self = self(), - spawn_link(fun() -> - S0 = ok(ssl:transport_accept(LS)), - {ok, S} = ssl:handshake(S0), - ok = ssl:setopts(S, [{active,N}]), - [{active,N}] = ok(ssl:getopts(S, [active])), - ssl:controlling_process(S, Self), - Self ! {server, S} - end), - C = ok(ssl:connect("localhost", Port, [{active,N}|ClientOpts])), - [{active,N}] = ok(ssl:getopts(C, [active])), - S = receive - {server, S0} -> S0 - after - 1000 -> - exit({error, connect}) - end, - active_n_common(C, N), - active_n_common(S, N), - ok = ssl:setopts(C, [{active,N}]), - ok = ssl:setopts(S, [{active,N}]), - ReceiveMsg = fun(Socket, Msg) -> - receive - {ssl,Socket,Msg} -> - ok; - {ssl,Socket,Begin} -> - receive - {ssl,Socket,End} -> - Msg = Begin ++ End, - ok - after 1000 -> - exit(timeout) - end - after 1000 -> - exit(timeout) - end - end, - repeat(3, fun(I) -> - Msg = "message "++integer_to_list(I), - ok = ssl:send(C, Msg), - ReceiveMsg(S, Msg), - ok = ssl:send(S, Msg), - ReceiveMsg(C, Msg) - end), - receive - {ssl_passive,S} -> - [{active,false}] = ok(ssl:getopts(S, [active])) - after - 1000 -> - exit({error,ssl_passive}) - end, - receive - {ssl_passive,C} -> - [{active,false}] = ok(ssl:getopts(C, [active])) - after - 1000 -> - exit({error,ssl_passive}) - end, - LS2 = ok(ssl:listen(0, [{active,0}])), - receive - {ssl_passive,LS2} -> - [{active,false}] = ok(ssl:getopts(LS2, [active])) - after - 1000 -> - exit({error,ssl_passive}) - end, - ok = ssl:close(LS2), - ok = ssl:close(C), - ok = ssl:close(S), - ok = ssl:close(LS), - ok. - -active_n_common(S, N) -> - ok = ssl:setopts(S, [{active,-N}]), - receive - {ssl_passive, S} -> ok - after - 1000 -> - error({error,ssl_passive_failure}) - end, - [{active,false}] = ok(ssl:getopts(S, [active])), - ok = ssl:setopts(S, [{active,0}]), - receive - {ssl_passive, S} -> ok - after - 1000 -> - error({error,ssl_passive_failure}) - end, - ok = ssl:setopts(S, [{active,32767}]), - {error,{options,_}} = ssl:setopts(S, [{active,1}]), - {error,{options,_}} = ssl:setopts(S, [{active,-32769}]), - ok = ssl:setopts(S, [{active,-32768}]), - receive - {ssl_passive, S} -> ok - after - 1000 -> - error({error,ssl_passive_failure}) - end, - [{active,false}] = ok(ssl:getopts(S, [active])), - ok = ssl:setopts(S, [{active,N}]), - ok = ssl:setopts(S, [{active,true}]), - [{active,true}] = ok(ssl:getopts(S, [active])), - receive - _ -> error({error,active_n}) - after - 0 -> - ok - end, - ok = ssl:setopts(S, [{active,N}]), - ok = ssl:setopts(S, [{active,once}]), - [{active,once}] = ok(ssl:getopts(S, [active])), - receive - _ -> error({error,active_n}) - after - 0 -> - ok - end, - {error,{options,_}} = ssl:setopts(S, [{active,32768}]), - ok = ssl:setopts(S, [{active,false}]), - [{active,false}] = ok(ssl:getopts(S, [active])), - ok. - -ok({ok,V}) -> V. - -repeat(N, Fun) -> - repeat(N, N, Fun). - -repeat(N, T, Fun) when is_integer(N), N > 0 -> - Fun(T-N), - repeat(N-1, T, Fun); -repeat(_, _, _) -> - ok. - -%%-------------------------------------------------------------------- -dh_params() -> - [{doc,"Test to specify DH-params file in server."}]. - -dh_params(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - DataDir = proplists:get_value(data_dir, Config), - DHParamFile = filename:join(DataDir, "dHParam.pem"), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, [{dhfile, DHParamFile} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, - [{ciphers,[{dhe_rsa,aes_256_cbc,sha}]} | - ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -tls_upgrade() -> - [{doc,"Test that you can upgrade an tcp connection to an ssl connection"}]. - -tls_upgrade(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - TcpOpts = [binary, {reuseaddr, true}], - - Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - upgrade_result, []}}, - {tcp_options, - [{active, false} | TcpOpts]}, - {ssl_options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_upgrade_client([{node, ClientNode}, - {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, upgrade_result, []}}, - {tcp_options, [binary]}, - {ssl_options, ClientOpts}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -upgrade_result(Socket) -> - ssl:setopts(Socket, [{active, true}]), - ok = ssl:send(Socket, "Hello world"), - %% Make sure binary is inherited from tcp socket and that we do - %% not get the list default! - receive - {ssl, _, <<"H">>} -> - receive - {ssl, _, <<"ello world">>} -> - ok - end; - {ssl, _, <<"Hello world">>} -> - ok - end. - - -%%-------------------------------------------------------------------- -internal_active_1() -> - [{doc,"Test internal active 1 (behave as internal active once)"}]. - -internal_active_1(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, [{active, true} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, [{active, true} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -tls_upgrade_with_timeout() -> - [{doc,"Test ssl_accept/3"}]. - -tls_upgrade_with_timeout(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - TcpOpts = [binary, {reuseaddr, true}], - - Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {timeout, 5000}, - {mfa, {?MODULE, - upgrade_result, []}}, - {tcp_options, - [{active, false} | TcpOpts]}, - {ssl_options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_upgrade_client([{node, ClientNode}, - {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, upgrade_result, []}}, - {tcp_options, TcpOpts}, - {ssl_options, ClientOpts}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -tls_downgrade() -> - [{doc,"Test that you can downgarde an ssl connection to an tcp connection"}]. -tls_downgrade(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, tls_downgrade_result, [self()]}}, - {options, [{active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, tls_downgrade_result, [self()]}}, - {options, [{active, false} |ClientOpts]}]), - - - ssl_test_lib:check_result(Server, ready, Client, ready), - - Server ! go, - Client ! go, - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -close_with_timeout() -> - [{doc,"Test normal (not downgrade) ssl:close/2"}]. -close_with_timeout(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, tls_close, []}}, - {options,[{active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, tls_close, []}}, - {options, [{active, false} |ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok). - - -%%-------------------------------------------------------------------- -tls_tcp_connect() -> - [{doc,"Test what happens when a tcp tries to connect, i,e. a bad (ssl) packet is sent first"}]. - -tls_tcp_connect(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - TcpOpts = [binary, {reuseaddr, true}, {active, false}], - - Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {timeout, 5000}, - {mfa, {?MODULE, dummy, []}}, - {tcp_options, TcpOpts}, - {ssl_options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), - ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]), - gen_tcp:send(Socket, "<SOME GARBLED NON SSL MESSAGE>"), - - receive - {tcp_closed, Socket} -> - receive - {Server, {error, Error}} -> - ct:log("Error ~p", [Error]) - end - end. -%%-------------------------------------------------------------------- -tls_tcp_connect_big() -> - [{doc,"Test what happens when a tcp tries to connect, i,e. a bad big (ssl) packet is sent first"}]. - -tls_tcp_connect_big(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - TcpOpts = [binary, {reuseaddr, true}], - - Rand = crypto:strong_rand_bytes(?MAX_CIPHER_TEXT_LENGTH+1), - Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {timeout, 5000}, - {mfa, {?MODULE, dummy, []}}, - {tcp_options, TcpOpts}, - {ssl_options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), - ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]), - - gen_tcp:send(Socket, <<?BYTE(0), - ?BYTE(3), ?BYTE(1), ?UINT16(?MAX_CIPHER_TEXT_LENGTH), Rand/binary>>), - - receive - {tcp_closed, Socket} -> - receive - {Server, {error, timeout}} -> - ct:fail("hangs"); - {Server, {error, Error}} -> - ct:log("Error ~p", [Error]); - {'EXIT', Server, _} -> - ok - end - end. - -%%-------------------------------------------------------------------- -ipv6() -> - [{require, ipv6_hosts}, - {doc,"Test ipv6."}]. -ipv6(Config) when is_list(Config) -> - {ok, Hostname0} = inet:gethostname(), - - case lists:member(list_to_atom(Hostname0), ct:get_config(ipv6_hosts)) of - true -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = - ssl_test_lib:run_where(Config, ipv6), - Server = ssl_test_lib:start_server([{node, ServerNode}, - {port, 0}, {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, - [inet6, {active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, - [inet6, {active, false} | ClientOpts]}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client); - false -> - {skip, "Host does not support IPv6"} - end. - -%%-------------------------------------------------------------------- - -invalid_keyfile() -> - [{doc,"Test what happens with an invalid key file"}]. -invalid_keyfile(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - BadOpts = ssl_test_lib:ssl_options(server_bad_key, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, BadOpts}]), - - Port = ssl_test_lib:inet_port(Server), - - Client = - ssl_test_lib:start_client_error([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {from, self()}, {options, ClientOpts}]), + {from, self()}, + {mfa, {?MODULE, protocol_info_result, []}}, + {options, ClientOpts}]), - File = proplists:get_value(keyfile,BadOpts), - ssl_test_lib:check_result(Server, {error,{options, {keyfile, File, {error,enoent}}}}, Client, - {error, closed}). + ServerMsg = ClientMsg = {ok,'tlsv1.2'}, + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg). %%-------------------------------------------------------------------- +unordered_protocol_versions_client() -> + [{doc,"Test that the highest protocol is selected even" + " when it is not first in the versions list."}]. -invalid_certfile() -> - [{doc,"Test what happens with an invalid cert file"}]. - -invalid_certfile(Config) when is_list(Config) -> +unordered_protocol_versions_client(Config) when is_list(Config) -> ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerBadOpts = ssl_test_lib:ssl_options(server_bad_cert, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, ServerBadOpts}]), - - Port = ssl_test_lib:inet_port(Server), - - Client = - ssl_test_lib:start_client_error([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {from, self()}, - {options, ClientOpts}]), - File = proplists:get_value(certfile, ServerBadOpts), - ssl_test_lib:check_result(Server, {error,{options, {certfile, File, {error,enoent}}}}, - Client, {error, closed}). - - -%%-------------------------------------------------------------------- -invalid_cacertfile() -> - [{doc,"Test what happens with an invalid cacert file"}]. - -invalid_cacertfile(Config) when is_list(Config) -> - ClientOpts = [{reuseaddr, true}|ssl_test_lib:ssl_options(client_opts, Config)], - ServerBadOpts = [{reuseaddr, true}|ssl_test_lib:ssl_options(server_bad_ca, Config)], - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server0 = - ssl_test_lib:start_server_error([{node, ServerNode}, - {port, 0}, {from, self()}, - {options, ServerBadOpts}]), - - Port0 = ssl_test_lib:inet_port(Server0), - - - Client0 = - ssl_test_lib:start_client_error([{node, ClientNode}, - {port, Port0}, {host, Hostname}, - {from, self()}, - {options, ClientOpts}]), - - File0 = proplists:get_value(cacertfile, ServerBadOpts), - - ssl_test_lib:check_result(Server0, {error, {options, {cacertfile, File0,{error,enoent}}}}, - Client0, {error, closed}), - - File = File0 ++ "do_not_exit.pem", - ServerBadOpts1 = [{cacertfile, File}|proplists:delete(cacertfile, ServerBadOpts)], - - Server1 = - ssl_test_lib:start_server_error([{node, ServerNode}, - {port, 0}, {from, self()}, - {options, ServerBadOpts1}]), - - Port1 = ssl_test_lib:inet_port(Server1), - - Client1 = - ssl_test_lib:start_client_error([{node, ClientNode}, - {port, Port1}, {host, Hostname}, - {from, self()}, - {options, ClientOpts}]), - - - ssl_test_lib:check_result(Server1, {error, {options, {cacertfile, File,{error,enoent}}}}, - Client1, {error, closed}), - ok. - - - -%%-------------------------------------------------------------------- -invalid_options() -> - [{doc,"Test what happens when we give invalid options"}]. - -invalid_options(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Check = fun(Client, Server, {versions, [sslv2, sslv3]} = Option) -> - ssl_test_lib:check_result(Server, - {error, {options, {sslv2, Option}}}, - Client, - {error, {options, {sslv2, Option}}}); - (Client, Server, Option) -> - ssl_test_lib:check_result(Server, - {error, {options, Option}}, - Client, - {error, {options, Option}}) - end, - - TestOpts = - [{versions, [sslv2, sslv3]}, - {verify, 4}, - {verify_fun, function}, - {fail_if_no_peer_cert, 0}, - {verify_client_once, 1}, - {depth, four}, - {certfile, 'cert.pem'}, - {keyfile,'key.pem' }, - {password, foo}, - {cacertfile, ""}, - {dhfile,'dh.pem' }, - {ciphers, [{foo, bar, sha, ignore}]}, - {reuse_session, foo}, - {reuse_sessions, 0}, - {renegotiate_at, "10"}, - {mode, depech}, - {packet, 8.0}, - {packet_size, "2"}, - {header, a}, - {active, trice}, - {key, 'key.pem' }], - - [begin - Server = - ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, [TestOpt | ServerOpts]}]), - %% Will never reach a point where port is used. - Client = - ssl_test_lib:start_client_error([{node, ClientNode}, {port, 0}, - {host, Hostname}, {from, self()}, - {options, [TestOpt | ClientOpts]}]), - Check(Client, Server, TestOpt), - ok - end || TestOpt <- TestOpts], - ok. + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), -%%-------------------------------------------------------------------- -tls_shutdown() -> - [{doc,"Test API function ssl:shutdown/2"}]. -tls_shutdown(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, tls_shutdown_result, [server]}}, - {options, [{exit_on_close, false}, - {active, false} | ServerOpts]}]), + {mfa, {?MODULE, protocol_info_result, []}}, + {options, ServerOpts }]), Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, - {?MODULE, tls_shutdown_result, [client]}}, - {options, - [{exit_on_close, false}, - {active, false} | ClientOpts]}]), - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -tls_shutdown_write() -> - [{doc,"Test API function ssl:shutdown/2 with option write."}]. -tls_shutdown_write(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, tls_shutdown_write_result, [server]}}, - {options, [{active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, tls_shutdown_write_result, [client]}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, {error, closed}). - -%%-------------------------------------------------------------------- -tls_shutdown_both() -> - [{doc,"Test API function ssl:shutdown/2 with option both."}]. -tls_shutdown_both(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, tls_shutdown_both_result, [server]}}, - {options, [{active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, tls_shutdown_both_result, [client]}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, {error, closed}). - -%%-------------------------------------------------------------------- -tls_shutdown_error() -> - [{doc,"Test ssl:shutdown/2 error handling"}]. -tls_shutdown_error(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - Port = ssl_test_lib:inet_port(node()), - {ok, Listen} = ssl:listen(Port, ServerOpts), - {error, enotconn} = ssl:shutdown(Listen, read_write), - ok = ssl:close(Listen), - {error, closed} = ssl:shutdown(Listen, read_write). - -%%-------------------------------------------------------------------- -default_reject_anonymous()-> - [{doc,"Test that by default anonymous cipher suites are rejected "}]. -default_reject_anonymous(Config) when is_list(Config) -> - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - Version = ssl_test_lib:protocol_version(Config), - TLSVersion = ssl_test_lib:tls_version(Version), - - [CipherSuite | _] = ssl_test_lib:ecdh_dh_anonymous_suites(TLSVersion), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, - [{ciphers,[CipherSuite]} | - ClientOpts]}]), - - ssl_test_lib:check_server_alert(Server, Client, insufficient_security). - -%%-------------------------------------------------------------------- -reuse_session() -> - [{doc,"Test reuse of sessions (short handshake)"}]. -reuse_session(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - - ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config). -%%-------------------------------------------------------------------- -reuse_session_expired() -> - [{doc,"Test sessions is not reused when it has expired"}]. -reuse_session_expired(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server0 = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {tcp_options, [{active, false}]}, - {options, ServerOpts}]), - Port0 = ssl_test_lib:inet_port(Server0), - - Client0 = ssl_test_lib:start_client([{node, ClientNode}, - {port, Port0}, {host, Hostname}, - {mfa, {ssl_test_lib, session_id, []}}, - {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), - Server0 ! listen, - - Client1 = ssl_test_lib:start_client([{node, ClientNode}, - {port, Port0}, {host, Hostname}, - {mfa, {ssl_test_lib, session_id, []}}, - {from, self()}, {options, ClientOpts}]), - - SID = receive - {Client0, Id0} -> - Id0 - end, - - receive - {Client1, SID} -> - ok - after ?SLEEP -> - ct:fail(session_not_reused) - end, - - Server0 ! listen, - - %% Make sure session is unregistered due to expiration - ct:sleep((?EXPIRE*2)), - - make_sure_expired(Hostname, Port0, SID), - - Client2 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port0}, {host, Hostname}, - {mfa, {ssl_test_lib, session_id, []}}, - {from, self()}, {options, ClientOpts}]), - receive - {Client2, SID} -> - ct:fail(session_reused_when_session_expired); - {Client2, _} -> - ok - end, - process_flag(trap_exit, false), - ssl_test_lib:close(Server0), - ssl_test_lib:close(Client0), - ssl_test_lib:close(Client1), - ssl_test_lib:close(Client2). - -make_sure_expired(Host, Port, Id) -> - {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), - [_, _,_, _, Prop] = StatusInfo, - State = ssl_test_lib:state(Prop), - ClientCache = element(2, State), - - case ssl_session_cache:lookup(ClientCache, {{Host, Port}, Id}) of - undefined -> - ok; - #session{is_resumable = false} -> - ok; - _ -> - ct:sleep(?SLEEP), - make_sure_expired(Host, Port, Id) - end. - -%%-------------------------------------------------------------------- -server_does_not_want_to_reuse_session() -> - [{doc,"Test reuse of sessions (short handshake)"}]. -server_does_not_want_to_reuse_session(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, [{reuse_session, fun(_,_,_,_) -> - false - end} | - ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - SessionInfo = - receive - {Server, Info} -> - Info - end, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - - %% Make sure session is registered - ct:sleep(?SLEEP), - ssl_test_lib:close(Client0), - - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - receive - {Client1, SessionInfo} -> - ct:fail(session_reused_when_server_does_not_want_to); - {Client1, _Other} -> - ok - end, - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- -client_renegotiate() -> - [{doc,"Test ssl:renegotiate/1 on client."}]. -client_renegotiate(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From erlang to erlang", - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - renegotiate, [Data]}}, - {options, [{reuse_sessions, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok, Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -client_secure_renegotiate() -> - [{doc,"Test ssl:renegotiate/1 on client."}]. -client_secure_renegotiate(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From erlang to erlang", - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, [{secure_renegotiate, true} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - renegotiate, [Data]}}, - {options, [{reuse_sessions, false}, - {secure_renegotiate, true}| ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok, Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -client_secure_renegotiate_fallback() -> - [{doc,"Test that we can set secure_renegotiate to false that is " - "fallback option, we however do not have a insecure server to test against!"}]. -client_secure_renegotiate_fallback(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From erlang to erlang", - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, [{secure_renegotiate, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), + {mfa, {?MODULE, protocol_info_result, []}}, + {options, [{versions, ['tlsv1.1', 'tlsv1.2']} | ClientOpts]}]), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - renegotiate, [Data]}}, - {options, [{reuse_sessions, false}, - {secure_renegotiate, false}| ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok, Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). + ServerMsg = ClientMsg = {ok, 'tlsv1.2'}, + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg). + +connect_dist() -> + [{doc,"Test a simple connect as is used by distribution"}]. -%%-------------------------------------------------------------------- -server_renegotiate() -> - [{doc,"Test ssl:renegotiate/1 on server."}]. -server_renegotiate(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), +connect_dist(Config) when is_list(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_kc_opts, Config), + ClientOpts = [{ssl_imp, new},{active, false}, {packet,4}|ClientOpts0], + ServerOpts0 = ssl_test_lib:ssl_options(server_kc_opts, Config), + ServerOpts = [{ssl_imp, new},{active, false}, {packet,4}|ServerOpts0], {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Data = "From erlang to erlang", - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, - renegotiate, [Data]}}, + {mfa, {?MODULE, connect_dist_s, []}}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, [{reuse_sessions, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -client_renegotiate_reused_session() -> - [{doc,"Test ssl:renegotiate/1 on client when the ssl session will be reused."}]. -client_renegotiate_reused_session(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From erlang to erlang", - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - renegotiate_reuse_session, [Data]}}, - {options, [{reuse_sessions, true} | ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok, Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). -%%-------------------------------------------------------------------- -server_renegotiate_reused_session() -> - [{doc,"Test ssl:renegotiate/1 on server when the ssl session will be reused."}]. -server_renegotiate_reused_session(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From erlang to erlang", - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - renegotiate_reuse_session, [Data]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, [{reuse_sessions, true} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). -%%-------------------------------------------------------------------- -client_no_wrap_sequence_number() -> - [{doc,"Test that erlang client will renegotiate session when", - "max sequence number celing is about to be reached. Although" - "in the testcase we use the test option renegotiate_at" - " to lower treashold substantially."}]. - -client_no_wrap_sequence_number(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - ErlData = "From erlang to erlang", - N = 12, - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Version = ssl_test_lib:protocol_version(Config, tuple), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - trigger_renegotiate, [[ErlData, treashold(N, Version)]]}}, - {options, [{reuse_sessions, false}, - {renegotiate_at, N} | ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -server_no_wrap_sequence_number() -> - [{doc, "Test that erlang server will renegotiate session when", - "max sequence number celing is about to be reached. Although" - "in the testcase we use the test option renegotiate_at" - " to lower treashold substantially."}]. - -server_no_wrap_sequence_number(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From erlang to erlang", - N = 12, - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - trigger_renegotiate, [[Data, N+2]]}}, - {options, [{renegotiate_at, N} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{reuse_sessions, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -der_input() -> - [{doc,"Test to input certs and key as der"}]. - -der_input(Config) when is_list(Config) -> - DataDir = proplists:get_value(data_dir, Config), - DHParamFile = filename:join(DataDir, "dHParam.pem"), - - {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), - [_, _,_, _, Prop] = StatusInfo, - State = ssl_test_lib:state(Prop), - [CADb | _] = element(6, State), - - Size = ets:info(CADb, size), - - SeverVerifyOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - {ServerCert, ServerKey, ServerCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} | - SeverVerifyOpts]), - ClientVerifyOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - {ClientCert, ClientKey, ClientCaCerts, DHParams} = der_input_opts([{dhfile, DHParamFile} | - ClientVerifyOpts]), - ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}, - {dh, DHParams}, - {cert, ServerCert}, {key, ServerKey}, {cacerts, ServerCaCerts}], - ClientOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true}, - {dh, DHParams}, - {cert, ClientCert}, {key, ClientKey}, {cacerts, ClientCaCerts}], - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - Size = ets:info(CADb, size). - -%%-------------------------------------------------------------------- -der_input_opts(Opts) -> - Certfile = proplists:get_value(certfile, Opts), - CaCertsfile = proplists:get_value(cacertfile, Opts), - Keyfile = proplists:get_value(keyfile, Opts), - Dhfile = proplists:get_value(dhfile, Opts), - [{_, Cert, _}] = ssl_test_lib:pem_to_der(Certfile), - [{Asn1Type, Key, _}] = ssl_test_lib:pem_to_der(Keyfile), - [{_, DHParams, _}] = ssl_test_lib:pem_to_der(Dhfile), - CaCerts = - lists:map(fun(Entry) -> - {_, CaCert, _} = Entry, - CaCert - end, ssl_test_lib:pem_to_der(CaCertsfile)), - {Cert, {Asn1Type, Key}, CaCerts, DHParams}. - -%%-------------------------------------------------------------------- -no_reuses_session_server_restart_new_cert() -> - [{doc,"Check that a session is not reused if the server is restarted with a new cert."}]. -no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> - - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - DsaServerOpts = ssl_test_lib:ssl_options(server_dsa_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - SessionInfo = - receive - {Server, Info} -> - Info - end, - - %% Make sure session is registered - ct:sleep(?SLEEP), - Monitor = erlang:monitor(process, Server), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client0), - receive - {'DOWN', Monitor, _, _, _} -> - ok - end, - - Server1 = - ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{reuseaddr, true} | DsaServerOpts]}]), - - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - receive - {Client1, SessionInfo} -> - ct:fail(session_reused_when_server_has_new_cert); - {Client1, _Other} -> - ok - end, - ssl_test_lib:close(Server1), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- -no_reuses_session_server_restart_new_cert_file() -> - [{doc,"Check that a session is not reused if a server is restarted with a new " - "cert contained in a file with the same name as the old cert."}]. - -no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, Config), - DsaServerOpts = ssl_test_lib:ssl_options(server_dsa_opts, Config), - PrivDir = proplists:get_value(priv_dir, Config), - - NewServerOpts0 = new_config(PrivDir, ServerOpts), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, NewServerOpts0}]), - Port = ssl_test_lib:inet_port(Server), - Client0 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - SessionInfo = - receive - {Server, Info} -> - Info - end, - - %% Make sure session is registered and we get - %% new file time stamp when calling new_config! - ct:sleep(?SLEEP* 2), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client0), - - ssl:clear_pem_cache(), - - NewServerOpts1 = new_config(PrivDir, DsaServerOpts), - - Server1 = - ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{reuseaddr, true} | NewServerOpts1]}]), - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - receive - {Client1, SessionInfo} -> - ct:fail(session_reused_when_server_has_new_cert); - {Client1, _Other} -> - ok - end, - ssl_test_lib:close(Server1), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- -defaults(Config) when is_list(Config)-> - Versions = ssl:versions(), - true = lists:member(sslv3, proplists:get_value(available, Versions)), - false = lists:member(sslv3, proplists:get_value(supported, Versions)), - true = lists:member('tlsv1', proplists:get_value(available, Versions)), - false = lists:member('tlsv1', proplists:get_value(supported, Versions)), - true = lists:member('tlsv1.1', proplists:get_value(available, Versions)), - false = lists:member('tlsv1.1', proplists:get_value(supported, Versions)), - true = lists:member('tlsv1.2', proplists:get_value(available, Versions)), - true = lists:member('tlsv1.2', proplists:get_value(supported, Versions)), - false = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites()), - true = lists:member({rsa,rc4_128,sha}, ssl:cipher_suites(all)), - false = lists:member({rsa,des_cbc,sha}, ssl:cipher_suites()), - true = lists:member({rsa,des_cbc,sha}, ssl:cipher_suites(all)), - false = lists:member({dhe_rsa,des_cbc,sha}, ssl:cipher_suites()), - true = lists:member({dhe_rsa,des_cbc,sha}, ssl:cipher_suites(all)), - true = lists:member('dtlsv1.2', proplists:get_value(available_dtls, Versions)), - true = lists:member('dtlsv1', proplists:get_value(available_dtls, Versions)), - true = lists:member('dtlsv1.2', proplists:get_value(supported_dtls, Versions)), - false = lists:member('dtlsv1', proplists:get_value(supported_dtls, Versions)). - -%%-------------------------------------------------------------------- -reuseaddr() -> - [{doc,"Test reuseaddr option"}]. - -reuseaddr(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{active, false} | ClientOpts]}]), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - - Server1 = - ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false} | ServerOpts]}]), - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Server1, ok, Client1, ok), - ssl_test_lib:close(Server1), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- -tls_tcp_reuseaddr() -> - [{doc, "Reference test case."}]. -tls_tcp_reuseaddr(Config) when is_list(Config) -> - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {transport, gen_tcp}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{active, false}, {reuseaddr, true}]}]), - Port = ssl_test_lib:inet_port(Server), - Client = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {transport, gen_tcp}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{active, false}]}]), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - - Server1 = - ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, - {from, self()}, - {transport, gen_tcp}, - {mfa, {?MODULE, tcp_send_recv_result, []}}, - {options, [{active, false}, {reuseaddr, true}]}]), - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {transport, gen_tcp}, - {mfa, {?MODULE, tcp_send_recv_result, []}}, - {options, [{active, false}]}]), - - ssl_test_lib:check_result(Server1, ok, Client1, ok), - ssl_test_lib:close(Server1), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- - -honor_server_cipher_order() -> - [{doc,"Test API honor server cipher order."}]. -honor_server_cipher_order(Config) when is_list(Config) -> - ClientCiphers = [#{key_exchange => dhe_rsa, - cipher => aes_128_cbc, - mac => sha, - prf => default_prf}, - #{key_exchange => dhe_rsa, - cipher => aes_256_cbc, - mac => sha, - prf => default_prf}], - ServerCiphers = [#{key_exchange => dhe_rsa, - cipher => aes_256_cbc, - mac =>sha, - prf => default_prf}, - #{key_exchange => dhe_rsa, - cipher => aes_128_cbc, - mac => sha, - prf => default_prf}], - honor_cipher_order(Config, true, ServerCiphers, ClientCiphers, #{key_exchange => dhe_rsa, - cipher => aes_256_cbc, - mac => sha, - prf => default_prf}). - -honor_client_cipher_order() -> - [{doc,"Test API honor server cipher order."}]. -honor_client_cipher_order(Config) when is_list(Config) -> - ClientCiphers = [#{key_exchange => dhe_rsa, - cipher => aes_128_cbc, - mac => sha, - prf => default_prf}, - #{key_exchange => dhe_rsa, - cipher => aes_256_cbc, - mac => sha, - prf => default_prf}], - ServerCiphers = [#{key_exchange => dhe_rsa, - cipher => aes_256_cbc, - mac =>sha, - prf => default_prf}, - #{key_exchange => dhe_rsa, - cipher => aes_128_cbc, - mac => sha, - prf => default_prf}], -honor_cipher_order(Config, false, ServerCiphers, ClientCiphers, #{key_exchange => dhe_rsa, - cipher => aes_128_cbc, - mac => sha, - prf => default_prf}). - -honor_cipher_order(Config, Honor, ServerCiphers, ClientCiphers, Expected) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, connection_info_result, []}}, - {options, [{ciphers, ServerCiphers}, {honor_cipher_order, Honor} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, connection_info_result, []}}, - {options, [{ciphers, ClientCiphers}, {honor_cipher_order, Honor} - | ClientOpts]}]), - - Version = ssl_test_lib:protocol_version(Config), - - ServerMsg = ClientMsg = {ok, {Version, Expected}}, - - ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -tls_ciphersuite_vs_version() -> - [{doc,"Test a SSLv3 client cannot negotiate a TLSv* cipher suite."}]. -tls_ciphersuite_vs_version(Config) when is_list(Config) -> - - {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]), - ok = gen_tcp:send(Socket, - <<22, 3,0, 49:16, % handshake, SSL 3.0, length - 1, 45:24, % client_hello, length - 3,0, % SSL 3.0 - 16#deadbeef:256, % 32 'random' bytes = 256 bits - 0, % no session ID - %% three cipher suites -- null, one with sha256 hash and one with sha hash - 6:16, 0,255, 0,61, 0,57, - 1, 0 % no compression - >>), - {ok, <<22, RecMajor:8, RecMinor:8, _RecLen:16, 2, HelloLen:24>>} = gen_tcp:recv(Socket, 9, 10000), - {ok, <<HelloBin:HelloLen/binary>>} = gen_tcp:recv(Socket, HelloLen, 5000), - ServerHello = tls_handshake:decode_handshake({RecMajor, RecMinor}, 2, HelloBin), - case ServerHello of - #server_hello{server_version = {3,0}, cipher_suite = <<0,57>>} -> - ok; - _ -> - ct:fail({unexpected_server_hello, ServerHello}) - end. - -%%-------------------------------------------------------------------- -conf_signature_algs() -> - [{doc,"Test to set the signature_algs option on both client and server"}]. -conf_signature_algs(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false}, {signature_algs, [{sha, rsa}]} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false}, {signature_algs, [{sha, rsa}]} | ClientOpts]}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - - -%%-------------------------------------------------------------------- -no_common_signature_algs() -> - [{doc,"Set the signature_algs option so that there client and server does not share any hash sign algorithms"}]. -no_common_signature_algs(Config) when is_list(Config) -> - - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, [{signature_algs, [{sha256, rsa}]} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{signature_algs, [{sha384, rsa}]} - | ClientOpts]}]), - - ssl_test_lib:check_server_alert(Server, Client, insufficient_security). - -%%-------------------------------------------------------------------- - -tls_dont_crash_on_handshake_garbage() -> - [{doc, "Ensure SSL server worker thows an alert on garbage during handshake " - "instead of crashing and exposing state to user code"}]. - -tls_dont_crash_on_handshake_garbage(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - unlink(Server), monitor(process, Server), - Port = ssl_test_lib:inet_port(Server), - - {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]), - - % Send hello and garbage record - ok = gen_tcp:send(Socket, - [<<22, 3,3, 49:16, 1, 45:24, 3,3, % client_hello - 16#deadbeef:256, % 32 'random' bytes = 256 bits - 0, 6:16, 0,255, 0,61, 0,57, 1, 0 >>, % some hello values - - <<22, 3,3, 5:16, 92,64,37,228,209>> % garbage - ]), - % Send unexpected change_cipher_spec - ok = gen_tcp:send(Socket, <<20, 3,3, 12:16, 111,40,244,7,137,224,16,109,197,110,249,152>>), - - % Ensure we receive an alert, not sudden disconnect - {ok, <<21, _/binary>>} = drop_handshakes(Socket, 1000). - -drop_handshakes(Socket, Timeout) -> - {ok, <<RecType:8, _RecMajor:8, _RecMinor:8, RecLen:16>> = Header} = gen_tcp:recv(Socket, 5, Timeout), - {ok, <<Frag:RecLen/binary>>} = gen_tcp:recv(Socket, RecLen, Timeout), - case RecType of - 22 -> drop_handshakes(Socket, Timeout); - _ -> {ok, <<Header/binary, Frag/binary>>} - end. - - -%%-------------------------------------------------------------------- - -hibernate() -> - [{doc,"Check that an SSL connection that is started with option " - "{hibernate_after, 1000} indeed hibernates after 1000ms of " - "inactivity"}]. - -hibernate(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - {Client, #sslsocket{pid=[Pid|_]}} = ssl_test_lib:start_client([return_socket, - {node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, [{hibernate_after, 1000}|ClientOpts]}]), - {current_function, _} = - process_info(Pid, current_function), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ct:sleep(1500), - {current_function, {erlang, hibernate, 3}} = - process_info(Pid, current_function), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -hibernate_right_away() -> - [{doc,"Check that an SSL connection that is configured to hibernate " - "after 0 or 1 milliseconds hibernates as soon as possible and not " - "crashes"}]. - -hibernate_right_away(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - StartServerOpts = [{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}], - StartClientOpts = [return_socket, - {node, ClientNode}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}], - - Server1 = ssl_test_lib:start_server(StartServerOpts), - Port1 = ssl_test_lib:inet_port(Server1), - {Client1, #sslsocket{pid = [Pid1|_]}} = ssl_test_lib:start_client(StartClientOpts ++ - [{port, Port1}, {options, [{hibernate_after, 0}|ClientOpts]}]), - - ssl_test_lib:check_result(Server1, ok, Client1, ok), - - ct:sleep(1000), %% Schedule out - - {current_function, {erlang, hibernate, 3}} = - process_info(Pid1, current_function), - ssl_test_lib:close(Server1), - ssl_test_lib:close(Client1), - - Server2 = ssl_test_lib:start_server(StartServerOpts), - Port2 = ssl_test_lib:inet_port(Server2), - {Client2, #sslsocket{pid = [Pid2|_]}} = ssl_test_lib:start_client(StartClientOpts ++ - [{port, Port2}, {options, [{hibernate_after, 1}|ClientOpts]}]), - - ssl_test_lib:check_result(Server2, ok, Client2, ok), - - ct:sleep(1000), %% Schedule out - - {current_function, {erlang, hibernate, 3}} = - process_info(Pid2, current_function), - - ssl_test_lib:close(Server2), - ssl_test_lib:close(Client2). - -%%-------------------------------------------------------------------- -listen_socket() -> - [{doc,"Check error handling and inet compliance when calling API functions with listen sockets."}]. - -listen_socket(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {ok, ListenSocket} = ssl:listen(0, ServerOpts), - - %% This can be a valid thing to do as - %% options are inherited by the accept socket - ok = ssl:controlling_process(ListenSocket, self()), - - {ok, _} = ssl:sockname(ListenSocket), - - {error, enotconn} = ssl:send(ListenSocket, <<"data">>), - {error, enotconn} = ssl:recv(ListenSocket, 0), - {error, enotconn} = ssl:connection_information(ListenSocket), - {error, enotconn} = ssl:peername(ListenSocket), - {error, enotconn} = ssl:peercert(ListenSocket), - {error, enotconn} = ssl:renegotiate(ListenSocket), - {error, enotconn} = ssl:prf(ListenSocket, 'master_secret', <<"Label">>, [client_random], 256), - {error, enotconn} = ssl:shutdown(ListenSocket, read_write), - - ok = ssl:close(ListenSocket). -%%-------------------------------------------------------------------- -tls_ssl_accept_timeout() -> - [{doc,"Test ssl:ssl_accept timeout"}]. - -tls_ssl_accept_timeout(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {timeout, 5000}, - {mfa, {ssl_test_lib, - no_result_msg, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - {ok, CSocket} = gen_tcp:connect(Hostname, Port, [binary, {active, true}]), - - receive - {tcp_closed, CSocket} -> - ssl_test_lib:check_result(Server, {error, timeout}), - receive - {'EXIT', Server, _} -> - %% Make sure supervisor had time to react on process exit - %% Could we come up with a better solution to this? - ct:sleep(500), - [] = supervisor:which_children(tls_connection_sup) - end - end. - -%%-------------------------------------------------------------------- -ssl_recv_timeout() -> - [{doc,"Test ssl:ssl_accept timeout"}]. - -ssl_recv_timeout(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, send_recv_result_timeout_server, []}}, - {options, [{active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - send_recv_result_timeout_client, []}}, - {options, [{active, false} | ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok, Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -connect_twice() -> - [{doc,""}]. -connect_twice(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{keepalive, true},{active, false} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{keepalive, true},{active, false} - | ClientOpts]}]), - Server ! listen, - - {Client1, #sslsocket{}} = - ssl_test_lib:start_client([return_socket, - {node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{keepalive, true},{active, false} - | ClientOpts]}]), - - ct:log("Testcase ~p, Client ~p Server ~p ~n", - [self(), Client, Server]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:check_result(Server, ok, Client1, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- -renegotiate_dos_mitigate_active() -> - [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row", - "immediately after each other"}]. -renegotiate_dos_mitigate_active(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - renegotiate_immediately, []}}, - {options, ClientOpts}]), - - ssl_test_lib:check_result(Client, ok, Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -renegotiate_dos_mitigate_passive() -> - [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row", - "immediately after each other"}]. -renegotiate_dos_mitigate_passive(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result, []}}, - {options, [{active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, {from, self()}, - {mfa, {?MODULE, - renegotiate_immediately, []}}, - {options, ClientOpts}]), - - ssl_test_lib:check_result(Client, ok, Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -renegotiate_dos_mitigate_absolute() -> - [{doc, "Mitigate DOS computational attack by not allowing client to initiate renegotiation"}]. -renegotiate_dos_mitigate_absolute(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, [{client_renegotiation, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - renegotiate_rejected, - []}}, + {mfa, {?MODULE, connect_dist_c, []}}, {options, ClientOpts}]), - - ssl_test_lib:check_result(Client, ok, Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -tls_tcp_error_propagation_in_active_mode() -> - [{doc,"Test that process recives {ssl_error, Socket, closed} when tcp error ocurres"}]. -tls_tcp_error_propagation_in_active_mode(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - {Client, #sslsocket{pid=[Pid|_]} = SslSocket} = ssl_test_lib:start_client([return_socket, - {node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, receive_msg, []}}, - {options, ClientOpts}]), - {status, _, _, StatusInfo} = sys:get_status(Pid), - [_, _,_, _, Prop] = StatusInfo, - State = ssl_test_lib:state(Prop), - StaticEnv = element(2, State), - Socket = element(11, StaticEnv), - %% Fake tcp error - Pid ! {tcp_error, Socket, etimedout}, - - ssl_test_lib:check_result(Client, {ssl_closed, SslSocket}). - -%%-------------------------------------------------------------------- -recv_error_handling() -> - [{doc,"Special case of call error handling"}]. -recv_error_handling(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, recv_close, []}}, - {options, [{active, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - {_Client, #sslsocket{} = SslSocket} = ssl_test_lib:start_client([return_socket, - {node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ClientOpts}]), - ssl:close(SslSocket), - ssl_test_lib:check_result(Server, ok). - - - -%%-------------------------------------------------------------------- -call_in_error_state() -> - [{doc,"Special case of call error handling"}]. -call_in_error_state(Config) when is_list(Config) -> - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = [{cacertfile, "foo.pem"} | proplists:delete(cacertfile, ServerOpts0)], - Pid = spawn_link(?MODULE, run_error_server, [[self() | ServerOpts]]), - receive - {Pid, Port} -> - spawn_link(?MODULE, run_client_error, [[Port, ClientOpts]]) - end, - receive - {error, closed} -> - ok; - Other -> - ct:fail(Other) - end. - -run_client_error([Port, Opts]) -> - ssl:connect("localhost", Port, Opts). - -run_error_server([ Pid | Opts]) -> - {ok, Listen} = ssl:listen(0, Opts), - {ok,{_, Port}} = ssl:sockname(Listen), - Pid ! {self(), Port}, - {ok, Socket} = ssl:transport_accept(Listen), - Pid ! ssl:controlling_process(Socket, self()). - -%%-------------------------------------------------------------------- - -close_in_error_state() -> - [{doc,"Special case of closing socket in error state"}]. -close_in_error_state(Config) when is_list(Config) -> - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), - ServerOpts = [{cacertfile, "foo.pem"} | proplists:delete(cacertfile, ServerOpts0)], - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - _ = spawn_link(?MODULE, run_error_server_close, [[self() | ServerOpts]]), - receive - {_Pid, Port} -> - spawn_link(?MODULE, run_client_error, [[Port, ClientOpts]]) - end, - receive - ok -> - ok; - Other -> - ct:fail(Other) - end. -%%-------------------------------------------------------------------- -abuse_transport_accept_socket() -> - [{doc,"Only ssl:handshake and ssl:controlling_process is allowed for transport_accept:sockets"}]. -abuse_transport_accept_socket(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + ssl_test_lib:check_result(Server, ok, Client, ok), - Server = ssl_test_lib:start_server_transport_abuse_socket([{node, ServerNode}, - {port, 0}, - {from, self()}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ClientOpts}]), - ssl_test_lib:check_result(Server, ok), ssl_test_lib:close(Server), ssl_test_lib:close(Client). - - -%%-------------------------------------------------------------------- -controlling_process_transport_accept_socket() -> - [{doc,"Only ssl:handshake and ssl:controlling_process is allowed for transport_accept:sockets"}]. -controlling_process_transport_accept_socket(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_transport_control([{node, ServerNode}, - {port, 0}, - {from, self()}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - _Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, ClientOpts}]), - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server). - -%%-------------------------------------------------------------------- -run_error_server_close([Pid | Opts]) -> - {ok, Listen} = ssl:listen(0, Opts), - {ok,{_, Port}} = ssl:sockname(Listen), - Pid ! {self(), Port}, - {ok, Socket} = ssl:transport_accept(Listen), - Pid ! ssl:close(Socket). - -%%-------------------------------------------------------------------- - -rizzo() -> - [{doc, "Test that there is a 1/n-1-split for non RC4 in 'TLS < 1.1' as it is - vunrable to Rizzo/Dungon attack"}]. - -rizzo(Config) when is_list(Config) -> - Prop = proplists:get_value(tc_group_properties, Config), - Version = proplists:get_value(name, Prop), - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(all, NVersion), - [{key_exchange, - fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa-> - true; - (_) -> - false - end}, - {cipher, - fun(rc4_128) -> - false; - (chacha20_poly1305) -> - false; - (_) -> - true - end}]), - - run_send_recv_rizzo(Ciphers, Config, Version, - {?MODULE, send_recv_result_active_rizzo, []}). -%%-------------------------------------------------------------------- -no_rizzo_rc4() -> - [{doc,"Test that there is no 1/n-1-split for RC4 as it is not vunrable to Rizzo/Dungon attack"}]. - -no_rizzo_rc4(Config) when is_list(Config) -> - Prop = proplists:get_value(tc_group_properties, Config), - Version = proplists:get_value(name, Prop), - NVersion = ssl_test_lib:protocol_version(Config, tuple), - %% Test uses RSA certs - Ciphers = ssl:filter_cipher_suites(ssl_test_lib:rc4_suites(NVersion), - [{key_exchange, - fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa-> - true; - (_) -> - false - end}]), - run_send_recv_rizzo(Ciphers, Config, Version, - {?MODULE, send_recv_result_active_no_rizzo, []}). - -rizzo_one_n_minus_one() -> - [{doc,"Test that the 1/n-1-split mitigation of Rizzo/Dungon attack can be explicitly selected"}]. - -rizzo_one_n_minus_one(Config) when is_list(Config) -> - Prop = proplists:get_value(tc_group_properties, Config), - Version = proplists:get_value(name, Prop), - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(all, NVersion), - [{key_exchange, - fun(Alg) when Alg == ecdh_rsa; Alg == ecdhe_rsa-> - true; - (_) -> - false - end}, - {cipher, - fun(rc4_128) -> - false; - %% TODO: remove this clause when chacha is fixed! - (chacha20_poly1305) -> - false; - (_) -> - true - end}]), - run_send_recv_rizzo(Ciphers, Config, Version, - {?MODULE, send_recv_result_active_rizzo, []}). - -rizzo_zero_n() -> - [{doc,"Test that the 0/n-split mitigation of Rizzo/Dungon attack can be explicitly selected"}]. - -rizzo_zero_n(Config) when is_list(Config) -> - Prop = proplists:get_value(tc_group_properties, Config), - Version = proplists:get_value(name, Prop), - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(default, NVersion), - [{cipher, - fun(rc4_128) -> - false; - (_) -> - true - end}]), - run_send_recv_rizzo(Ciphers, Config, Version, - {?MODULE, send_recv_result_active_no_rizzo, []}). - -rizzo_disabled() -> - [{doc,"Test that the mitigation of Rizzo/Dungon attack can be explicitly disabled"}]. - -rizzo_disabled(Config) when is_list(Config) -> - Prop = proplists:get_value(tc_group_properties, Config), - Version = proplists:get_value(name, Prop), - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(default, NVersion), - [{cipher, - fun(rc4_128) -> - false; - (_) -> - true - end}]), - run_send_recv_rizzo(Ciphers, Config, Version, - {?MODULE, send_recv_result_active_no_rizzo, []}). - -%%-------------------------------------------------------------------- -new_server_wants_peer_cert() -> - [{doc, "Test that server configured to do client certification does" - " not reuse session without a client certificate."}]. -new_server_wants_peer_cert(Config) when is_list(Config) -> - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - VServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} - | ssl_test_lib:ssl_options(server_verification_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, peercert_result, []}}, - {options, [ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ClientOpts}]), - - Monitor = erlang:monitor(process, Server), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - receive - {'DOWN', Monitor, _, _, _} -> - ok - end, - - Server1 = ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, - {from, self()}, - {mfa, {?MODULE, peercert_result, []}}, - {options, VServerOpts}]), - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [ClientOpts]}]), - - CertFile = proplists:get_value(certfile, ClientOpts), - [{'Certificate', BinCert, _}]= ssl_test_lib:pem_to_der(CertFile), - - ServerMsg = {error, no_peercert}, - Sever1Msg = {ok, BinCert}, - - ssl_test_lib:check_result(Server, ServerMsg, Server1, Sever1Msg), - - ssl_test_lib:close(Server1), - ssl_test_lib:close(Client), - ssl_test_lib:close(Client1). - -%%-------------------------------------------------------------------- -session_cache_process_list() -> - [{doc,"Test reuse of sessions (short handshake)"}]. -session_cache_process_list(Config) when is_list(Config) -> - session_cache_process(list,Config). -%%-------------------------------------------------------------------- -session_cache_process_mnesia() -> - [{doc,"Test reuse of sessions (short handshake)"}]. -session_cache_process_mnesia(Config) when is_list(Config) -> - session_cache_process(mnesia,Config). +eccs() -> + [{doc, "Test API functions eccs/0 and eccs/1"}]. -%%-------------------------------------------------------------------- +eccs(Config) when is_list(Config) -> + [_|_] = All = ssl:eccs(), + [] = ssl:eccs(sslv3), + [_|_] = Tls = ssl:eccs(tlsv1), + [_|_] = Tls1 = ssl:eccs('tlsv1.1'), + [_|_] = Tls2 = ssl:eccs('tlsv1.2'), + [_|_] = Tls1 = ssl:eccs('dtlsv1'), + [_|_] = Tls2 = ssl:eccs('dtlsv1.2'), + %% ordering is currently not verified by the test + true = lists:sort(All) =:= lists:usort(Tls ++ Tls1 ++ Tls2), + ok. tls_versions_option() -> [{doc,"Test API versions option to connect/listen."}]. @@ -4295,1468 +452,6 @@ tls_versions_option(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -unordered_protocol_versions_server() -> - [{doc,"Test that the highest protocol is selected even" - " when it is not first in the versions list."}]. - -unordered_protocol_versions_server(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, protocol_info_result, []}}, - {options, [{versions, ['tlsv1.1', 'tlsv1.2']} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, protocol_info_result, []}}, - {options, ClientOpts}]), - - ServerMsg = ClientMsg = {ok,'tlsv1.2'}, - ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg). - -%%-------------------------------------------------------------------- -unordered_protocol_versions_client() -> - [{doc,"Test that the highest protocol is selected even" - " when it is not first in the versions list."}]. - -unordered_protocol_versions_client(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, protocol_info_result, []}}, - {options, ServerOpts }]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, protocol_info_result, []}}, - {options, [{versions, ['tlsv1.1', 'tlsv1.2']} | ClientOpts]}]), - - ServerMsg = ClientMsg = {ok, 'tlsv1.2'}, - ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg). - -%%-------------------------------------------------------------------- -max_handshake_size() -> - [{doc,"Test that we can set max_handshake_size to max value."}]. - -max_handshake_size(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, [{max_handshake_size, 8388607} |ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, [{max_handshake_size, 8388607} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok). - -%%-------------------------------------------------------------------- - -server_name_indication_option() -> - [{doc,"Test API server_name_indication option to connect."}]. -server_name_indication_option(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, - [{server_name_indication, disable} | - ClientOpts]} - ]), - - ssl_test_lib:check_result(Server, ok, Client0, ok), - Server ! listen, - - Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, - [{server_name_indication, Hostname} | ClientOpts] - }]), - ssl_test_lib:check_result(Server, ok, Client1, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client0), - ssl_test_lib:close(Client1). -%%-------------------------------------------------------------------- - -accept_pool() -> - [{doc,"Test having an accept pool."}]. -accept_pool(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server0 = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {accepters, 3}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server0), - [Server1, Server2] = ssl_test_lib:accepters(2), - - Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ClientOpts} - ]), - - Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ClientOpts} - ]), - - Client2 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ClientOpts} - ]), - - ssl_test_lib:check_ok([Server0, Server1, Server2, Client0, Client1, Client2]), - - ssl_test_lib:close(Server0), - ssl_test_lib:close(Server1), - ssl_test_lib:close(Server2), - ssl_test_lib:close(Client0), - ssl_test_lib:close(Client1), - ssl_test_lib:close(Client2). - -%%-------------------------------------------------------------------- -%% TLS 1.3 -%%-------------------------------------------------------------------- - -tls13_enable_client_side() -> - [{doc,"Test that a TLS 1.3 client can connect to a TLS 1.2 server."}]. - -tls13_enable_client_side(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, protocol_info_result, []}}, - {options, [{versions, - ['tlsv1.1', 'tlsv1.2']} | ServerOpts] }]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, protocol_info_result, []}}, - {options, [{versions, - ['tlsv1.2', 'tlsv1.3']} | ClientOpts]}]), - - ServerMsg = ClientMsg = {ok, 'tlsv1.2'}, - ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg). - -tls13_enable_server_side() -> - [{doc,"Test that a TLS 1.2 client can connect to a TLS 1.3 server."}]. - -tls13_enable_server_side(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, protocol_info_result, []}}, - {options, [{versions, - ['tlsv1.2', 'tlsv1.3']} | ServerOpts] }]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, protocol_info_result, []}}, - {options, [{versions, - ['tlsv1.2', 'tlsv1.1']} | ClientOpts]}]), - - ServerMsg = ClientMsg = {ok, 'tlsv1.2'}, - ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg). - -tls_record_1_3_encode_decode() -> - [{doc,"Test TLS 1.3 record encode/decode functions"}]. - -tls_record_1_3_encode_decode(_Config) -> - ConnectionStates = - #{current_read => - #{beast_mitigation => one_n_minus_one, - cipher_state => - {cipher_state, - <<14,172,111,243,199,170,242,203,126,205,34,93,122,115,226,14, - 15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>, - <<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228, - 131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>, - undefined,undefined,undefined,16}, - client_verify_data => undefined,compression_state => undefined, - mac_secret => undefined,secure_renegotiation => undefined, - security_parameters => - {security_parameters, - <<19,2>>, - 0,8,2,undefined,undefined,undefined,undefined,undefined, - sha384,undefined,undefined, - {handshake_secret, - <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121, - 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218, - 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56, - 157>>}, - undefined, - <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207, - 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>, - undefined}, - sequence_number => 0,server_verify_data => undefined}, - current_write => - #{beast_mitigation => one_n_minus_one, - cipher_state => - {cipher_state, - <<14,172,111,243,199,170,242,203,126,205,34,93,122,115,226,14, - 15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>, - <<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228, - 131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>, - undefined,undefined,undefined,16}, - client_verify_data => undefined,compression_state => undefined, - mac_secret => undefined,secure_renegotiation => undefined, - security_parameters => - {security_parameters, - <<19,2>>, - 0,8,2,undefined,undefined,undefined,undefined,undefined, - sha384,undefined,undefined, - {handshake_secret, - <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121, - 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218, - 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56, - 157>>}, - undefined, - <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207, - 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>, - undefined}, - sequence_number => 0,server_verify_data => undefined}}, - - PlainText = [11, - <<0,2,175>>, - <<0,0,2,171,0,2,166,48,130,2,162,48,130,1,138,2,9,0,186,57,220,137,88,255, - 191,235,48,13,6,9,42,134,72,134,247,13,1,1,11,5,0,48,18,49,16,48,14,6,3,85, - 4,3,12,7,84,101,115,116,32,67,65,48,30,23,13,49,56,48,53,48,52,49,52,49,50, - 51,56,90,23,13,50,56,48,50,48,52,49,52,49,50,51,56,90,48,20,49,18,48,16,6, - 3,85,4,3,12,9,108,111,99,97,108,104,111,115,116,48,130,1,34,48,13,6,9,42, - 134,72,134,247,13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,169,40, - 144,176,121,63,134,97,144,126,243,183,225,157,37,131,183,225,87,243,23,88, - 230,70,9,134,32,147,7,27,167,98,51,81,224,75,199,12,229,251,195,207,75,179, - 181,78,128,3,255,44,58,39,43,172,142,45,186,58,51,65,187,199,154,153,245, - 70,133,137,1,27,87,42,116,65,251,129,109,145,233,97,171,71,54,213,185,74, - 209,166,11,218,189,119,206,86,170,60,212,213,85,189,30,50,215,23,185,53, - 132,238,132,176,198,250,139,251,198,221,225,128,109,113,23,220,39,143,71, - 30,59,189,51,244,61,158,214,146,180,196,103,169,189,221,136,78,129,216,148, - 2,9,8,65,37,224,215,233,13,209,21,235,20,143,33,74,59,53,208,90,152,94,251, - 54,114,171,39,88,230,227,158,211,135,37,182,67,205,161,59,20,138,58,253,15, - 53,48,8,157,9,95,197,9,177,116,21,54,9,125,78,109,182,83,20,16,234,223,116, - 41,155,123,87,77,17,120,153,246,239,124,130,105,219,166,146,242,151,66,198, - 75,72,63,28,246,86,16,244,223,22,36,50,15,247,222,98,6,152,136,154,72,150, - 73,127,2,3,1,0,1,48,13,6,9,42,134,72,134,247,13,1,1,11,5,0,3,130,1,1,0,76, - 33,54,160,229,219,219,193,150,116,245,252,18,39,235,145,86,12,167,171,52, - 117,166,30,83,5,216,245,177,217,247,95,1,136,94,246,212,108,248,230,111, - 225,202,189,6,129,8,70,128,245,18,204,215,87,82,129,253,227,122,66,182,184, - 189,30,193,169,144,218,216,109,105,110,215,144,60,104,162,178,101,164,218, - 122,60,37,41,143,57,150,52,59,51,112,238,113,239,168,114,69,183,143,154,73, - 61,58,80,247,172,95,251,55,28,186,28,200,206,230,118,243,92,202,189,49,76, - 124,252,76,0,247,112,85,194,69,59,222,163,228,103,49,110,104,109,251,155, - 138,9,37,167,49,189,48,134,52,158,185,129,24,96,153,196,251,90,206,76,239, - 175,119,174,165,133,108,222,125,237,125,187,149,152,83,190,16,202,94,202, - 201,40,218,22,254,63,189,41,174,97,140,203,70,18,196,118,237,175,134,79,78, - 246,2,61,54,77,186,112,32,17,193,192,188,217,252,215,200,7,245,180,179,132, - 183,212,229,155,15,152,206,135,56,81,88,3,123,244,149,110,182,72,109,70,62, - 146,152,146,151,107,126,216,210,9,93,0,0>>], - - {[_Header|Encoded], _} = tls_record_1_3:encode_plain_text(22, PlainText, ConnectionStates), - CipherText = #ssl_tls{type = 23, version = {3,3}, fragment = Encoded}, - - {#ssl_tls{type = 22, version = {3,4}, fragment = DecodedText}, _} = - tls_record_1_3:decode_cipher_text(CipherText, ConnectionStates), - - DecodedText = iolist_to_binary(PlainText), - ct:log("Decoded: ~p ~n", [DecodedText]), - ok. - -tls13_1_RTT_handshake() -> - [{doc,"Test TLS 1.3 1-RTT Handshake"}]. - -tls13_1_RTT_handshake(_Config) -> - %% ConnectionStates with NULL cipher - ConnStatesNull = - #{current_write => - #{security_parameters => - #security_parameters{cipher_suite = ?TLS_NULL_WITH_NULL_NULL}, - sequence_number => 0 - } - }, - - %% {client} construct a ClientHello handshake message: - %% - %% ClientHello (196 octets): 01 00 00 c0 03 03 cb 34 ec b1 e7 81 63 - %% ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 - %% 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b - %% 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 - %% 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23 - %% 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2 - %% 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a - %% af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 - %% 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 - %% 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 - %% - %% {client} send handshake record: - %% - %% payload (196 octets): 01 00 00 c0 03 03 cb 34 ec b1 e7 81 63 ba - %% 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 02 - %% 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b 00 - %% 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 - %% 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23 00 - %% 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2 3d - %% 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af - %% 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 - %% 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 - %% 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 - %% - %% complete record (201 octets): 16 03 01 00 c4 01 00 00 c0 03 03 cb - %% 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 - %% ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 - %% 00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 - %% 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 - %% 01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d - %% e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d - %% 54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e - %% 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 - %% 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 - ClientHello = - hexstr2bin("01 00 00 c0 03 03 cb 34 ec b1 e7 81 63 - ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 - 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b - 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 - 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23 - 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2 - 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a - af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 - 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 - 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"), - - ClientHelloRecord = - %% Current implementation always sets - %% legacy_record_version to Ox0303 - hexstr2bin("16 03 03 00 c4 01 00 00 c0 03 03 cb - 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 - ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 - 00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 - 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 - 01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d - e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d - 54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e - 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 - 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"), - - {CHEncrypted, _} = - tls_record:encode_handshake(ClientHello, {3,4}, ConnStatesNull), - ClientHelloRecord = iolist_to_binary(CHEncrypted), - - %% {server} extract secret "early": - %% - %% salt: 0 (all zero octets) - %% - %% IKM (32 octets): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - %% 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - %% - %% secret (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c - %% e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a - HKDFAlgo = sha256, - Salt = binary:copy(<<?BYTE(0)>>, 32), - IKM = binary:copy(<<?BYTE(0)>>, 32), - EarlySecret = - hexstr2bin("33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c - e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a"), - - {early_secret, EarlySecret} = tls_v1:key_schedule(early_secret, HKDFAlgo, {psk, Salt}), - - %% {client} create an ephemeral x25519 key pair: - %% - %% private key (32 octets): 49 af 42 ba 7f 79 94 85 2d 71 3e f2 78 - %% 4b cb ca a7 91 1d e2 6a dc 56 42 cb 63 45 40 e7 ea 50 05 - %% - %% public key (32 octets): 99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d - %% ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c - CPublicKey = - hexstr2bin("99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d - ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c"), - - %% {server} create an ephemeral x25519 key pair: - %% - %% private key (32 octets): b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56 - %% 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e - %% - %% public key (32 octets): c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 - %% 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f - SPrivateKey = - hexstr2bin("b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56 - 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e"), - - SPublicKey = - hexstr2bin("c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 - 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f"), - - %% {server} construct a ServerHello handshake message: - %% - %% ServerHello (90 octets): 02 00 00 56 03 03 a6 af 06 a4 12 18 60 - %% dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e - %% d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88 - %% 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 - %% dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04 - ServerHello = - hexstr2bin("02 00 00 56 03 03 a6 af 06 a4 12 18 60 - dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e - d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88 - 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 - dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"), - - %% {server} derive secret for handshake "tls13 derived": - %% - %% PRK (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c e2 - %% 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a - %% - %% hash (32 octets): e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 - %% 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55 - %% - %% info (49 octets): 00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64 - %% 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 - %% 64 9b 93 4c a4 95 99 1b 78 52 b8 55 - %% - %% expanded (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba - %% b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba - Hash = - hexstr2bin("e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 - 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55"), - - Hash = crypto:hash(HKDFAlgo, <<>>), - - Info = - hexstr2bin("00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64 - 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 - 64 9b 93 4c a4 95 99 1b 78 52 b8 55"), - - Info = tls_v1:create_info(<<"derived">>, Hash, ssl_cipher:hash_size(HKDFAlgo)), - - Expanded = - hexstr2bin("6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba - b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba"), - - Expanded = tls_v1:derive_secret(EarlySecret, <<"derived">>, <<>>, HKDFAlgo), - - %% {server} extract secret "handshake": - %% - %% salt (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba b6 97 - %% 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba - %% - %% IKM (32 octets): 8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d - %% 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d - %% - %% secret (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b - %% 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac - - %% salt = Expanded - HandshakeIKM = - hexstr2bin("8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d - 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d"), - - HandshakeSecret = - hexstr2bin("1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b - 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac"), - - HandshakeIKM = crypto:compute_key(ecdh, CPublicKey, SPrivateKey, x25519), - - {handshake_secret, HandshakeSecret} = - tls_v1:key_schedule(handshake_secret, HKDFAlgo, HandshakeIKM, - {early_secret, EarlySecret}), - - %% {server} derive secret "tls13 c hs traffic": - %% - %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 - %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac - %% - %% hash (32 octets): 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed - %% d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 - %% - %% info (54 octets): 00 20 12 74 6c 73 31 33 20 63 20 68 73 20 74 72 - %% 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 - %% ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 - %% - %% expanded (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e - %% 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21 - - %% PRK = HandshakeSecret - CHSTHash = - hexstr2bin("86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed - d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"), - - CHSTInfo = - hexstr2bin("00 20 12 74 6c 73 31 33 20 63 20 68 73 20 74 72 - 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 - ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"), - - CHSTrafficSecret = - hexstr2bin(" b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e - 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21"), - - CHSH = <<ClientHello/binary,ServerHello/binary>>, - CHSTHash = crypto:hash(HKDFAlgo, CHSH), - CHSTInfo = tls_v1:create_info(<<"c hs traffic">>, CHSTHash, ssl_cipher:hash_size(HKDFAlgo)), - - CHSTrafficSecret = - tls_v1:client_handshake_traffic_secret(HKDFAlgo, {handshake_secret, HandshakeSecret}, CHSH), - - %% {server} derive secret "tls13 s hs traffic": - %% - %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 - %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac - %% - %% hash (32 octets): 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed - %% d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 - %% - %% info (54 octets): 00 20 12 74 6c 73 31 33 20 73 20 68 73 20 74 72 - %% 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 - %% ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 - %% - %% expanded (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d - %% 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 - - %% PRK = HandshakeSecret - %% hash = CHSTHash - SHSTInfo = - hexstr2bin("00 20 12 74 6c 73 31 33 20 73 20 68 73 20 74 72 - 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 - ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"), - - SHSTrafficSecret = - hexstr2bin("b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d - 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38"), - - SHSTInfo = tls_v1:create_info(<<"s hs traffic">>, CHSTHash, ssl_cipher:hash_size(HKDFAlgo)), - - SHSTrafficSecret = - tls_v1:server_handshake_traffic_secret(HKDFAlgo, {handshake_secret, HandshakeSecret}, CHSH), - - - %% {server} derive secret for master "tls13 derived": - %% - %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 - %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac - %% - %% hash (32 octets): e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 - %% 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55 - %% - %% info (49 octets): 00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64 - %% 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 - %% 64 9b 93 4c a4 95 99 1b 78 52 b8 55 - %% - %% expanded (32 octets): 43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 - %% 90 b5 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4 - - %% PRK = HandshakeSecret - %% hash = Hash - %% info = Info - MasterDeriveSecret = - hexstr2bin("43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 - 90 b5 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4"), - - MasterDeriveSecret = tls_v1:derive_secret(HandshakeSecret, <<"derived">>, <<>>, HKDFAlgo), - - %% {server} extract secret "master": - %% - %% salt (32 octets): 43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 90 b5 - %% 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4 - %% - %% IKM (32 octets): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - %% 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 - %% - %% secret (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a - %% 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 - - %% salt = MasterDeriveSecret - %% IKM = IKM - MasterSecret = - hexstr2bin("18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a - 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19"), - - {master_secret, MasterSecret} = - tls_v1:key_schedule(master_secret, HKDFAlgo, {handshake_secret, HandshakeSecret}), - - %% {server} send handshake record: - %% - %% payload (90 octets): 02 00 00 56 03 03 a6 af 06 a4 12 18 60 dc 5e - %% 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e d3 e2 - %% 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88 76 11 - %% 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 - %% b1 b0 4e 75 1f 0f 00 2b 00 02 03 04 - %% - %% complete record (95 octets): 16 03 03 00 5a 02 00 00 56 03 03 a6 - %% af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 - %% 34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 - %% 1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 - %% cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04 - - %% payload = ServerHello - ServerHelloRecord = - hexstr2bin("16 03 03 00 5a 02 00 00 56 03 03 a6 - af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 - 34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 - 1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 - cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"), - - {SHEncrypted, _} = - tls_record:encode_handshake(ServerHello, {3,4}, ConnStatesNull), - ServerHelloRecord = iolist_to_binary(SHEncrypted), - - %% {server} derive write traffic keys for handshake data: - %% - %% PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 - %% e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 - %% - %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 - %% - %% key expanded (16 octets): 3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e - %% e4 03 bc - %% - %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 - %% - %% iv expanded (12 octets): 5d 31 3e b2 67 12 76 ee 13 00 0b 30 - - %% PRK = SHSTrafficSecret - WriteKeyInfo = - hexstr2bin("00 10 09 74 6c 73 31 33 20 6b 65 79 00"), - - WriteKey = - hexstr2bin("3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e e4 03 bc"), - - WriteIVInfo = - hexstr2bin("00 0c 08 74 6c 73 31 33 20 69 76 00"), - - WriteIV = - hexstr2bin(" 5d 31 3e b2 67 12 76 ee 13 00 0b 30"), - - Cipher = aes_128_gcm, %% TODO: get from ServerHello - - WriteKeyInfo = tls_v1:create_info(<<"key">>, <<>>, ssl_cipher:key_material(Cipher)), - %% TODO: remove hardcoded IV size - WriteIVInfo = tls_v1:create_info(<<"iv">>, <<>>, 12), - - {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, SHSTrafficSecret), - - %% {server} construct an EncryptedExtensions handshake message: - %% - %% EncryptedExtensions (40 octets): 08 00 00 24 00 22 00 0a 00 14 00 - %% 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c - %% 00 02 40 01 00 00 00 00 - %% - %% {server} construct a Certificate handshake message: - %% - %% Certificate (445 octets): 0b 00 01 b9 00 00 01 b5 00 01 b0 30 82 - %% 01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48 - %% 86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03 - %% 72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17 - %% 0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06 - %% 03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7 - %% 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f - %% 82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26 - %% d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c - %% 1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52 - %% 4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74 - %% 80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93 - %% ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03 - %% 01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06 - %% 03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01 - %% 01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a - %% 72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea - %% e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01 - %% 51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be - %% c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b - %% 1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8 - %% 96 12 29 ac 91 87 b4 2b 4d e1 00 00 - %% - %% {server} construct a CertificateVerify handshake message: - %% - %% CertificateVerify (136 octets): 0f 00 00 84 08 04 00 80 5a 74 7c - %% 5d 88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a - %% b3 ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07 - %% 86 53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b - %% be 8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44 - %% 5c 9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a - %% 3d a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3 - EncryptedExtensions = - hexstr2bin("08 00 00 24 00 22 00 0a 00 14 00 - 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c - 00 02 40 01 00 00 00 00"), - - Certificate = - hexstr2bin("0b 00 01 b9 00 00 01 b5 00 01 b0 30 82 - 01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48 - 86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03 - 72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17 - 0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06 - 03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7 - 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f - 82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26 - d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c - 1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52 - 4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74 - 80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93 - ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03 - 01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06 - 03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01 - 01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a - 72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea - e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01 - 51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be - c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b - 1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8 - 96 12 29 ac 91 87 b4 2b 4d e1 00 00"), - - CertificateVerify = - hexstr2bin("0f 00 00 84 08 04 00 80 5a 74 7c - 5d 88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a - b3 ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07 - 86 53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b - be 8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44 - 5c 9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a - 3d a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3"), - - %% {server} calculate finished "tls13 finished": - %% - %% PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 - %% e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 - %% - %% hash (0 octets): (empty) - %% - %% info (18 octets): 00 20 0e 74 6c 73 31 33 20 66 69 6e 69 73 68 65 - %% 64 00 - %% - %% expanded (32 octets): 00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85 - %% c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8 - %% - %% finished (32 octets): 9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4 - %% de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18 - - %% PRK = SHSTrafficSecret - FInfo = - hexstr2bin("00 20 0e 74 6c 73 31 33 20 66 69 6e 69 73 68 65 - 64 00"), - - FExpanded = - hexstr2bin("00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85 - c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8"), - - FinishedVerifyData = - hexstr2bin("9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4 - de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18"), - - FInfo = tls_v1:create_info(<<"finished">>, <<>>, ssl_cipher:hash_size(HKDFAlgo)), - - FExpanded = tls_v1:finished_key(SHSTrafficSecret, HKDFAlgo), - - MessageHistory0 = [CertificateVerify, - Certificate, - EncryptedExtensions, - ServerHello, - ClientHello], - - FinishedVerifyData = tls_v1:finished_verify_data(FExpanded, HKDFAlgo, MessageHistory0), - - %% {server} construct a Finished handshake message: - %% - %% Finished (36 octets): 14 00 00 20 9b 9b 14 1d 90 63 37 fb d2 cb - %% dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 - %% 18 - FinishedHSBin = - hexstr2bin("14 00 00 20 9b 9b 14 1d 90 63 37 fb d2 cb - dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 - 18"), - - FinishedHS = #finished{verify_data = FinishedVerifyData}, - - FinishedIOList = tls_handshake:encode_handshake(FinishedHS, {3,4}), - FinishedHSBin = iolist_to_binary(FinishedIOList), - - %% {server} derive secret "tls13 c ap traffic": - %% - %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 - %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 - %% - %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a - %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 - %% - %% info (54 octets): 00 20 12 74 6c 73 31 33 20 63 20 61 70 20 74 72 - %% 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b - %% 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 - %% - %% expanded (32 octets): 9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce - %% 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5 - - %% PRK = MasterSecret - CAPTHash = - hexstr2bin("96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a - 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"), - CAPTInfo = - hexstr2bin("00 20 12 74 6c 73 31 33 20 63 20 61 70 20 74 72 - 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b - 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"), - - CAPTrafficSecret = - hexstr2bin("9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce - 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5"), - - CHSF = <<ClientHello/binary, - ServerHello/binary, - EncryptedExtensions/binary, - Certificate/binary, - CertificateVerify/binary, - FinishedHSBin/binary>>, - - CAPTHash = crypto:hash(HKDFAlgo, CHSF), - - CAPTInfo = - tls_v1:create_info(<<"c ap traffic">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)), - - CAPTrafficSecret = - tls_v1:client_application_traffic_secret_0(HKDFAlgo, {master_secret, MasterSecret}, CHSF), - - %% {server} derive secret "tls13 s ap traffic": - %% - %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 - %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 - %% - %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a - %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 - %% - %% info (54 octets): 00 20 12 74 6c 73 31 33 20 73 20 61 70 20 74 72 - %% 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b - %% 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 - %% - %% expanded (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 - %% 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43 - - %% PRK = MasterSecret - %% hash = CAPTHash - SAPTInfo = - hexstr2bin(" 00 20 12 74 6c 73 31 33 20 73 20 61 70 20 74 72 - 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b - 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"), - - SAPTrafficSecret = - hexstr2bin("a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 - 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43"), - - SAPTInfo = - tls_v1:create_info(<<"s ap traffic">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)), - - SAPTrafficSecret = - tls_v1:server_application_traffic_secret_0(HKDFAlgo, {master_secret, MasterSecret}, CHSF), - - %% {server} derive secret "tls13 exp master": - %% - %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 - %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 - %% - %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a - %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 - %% - %% info (52 octets): 00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73 - %% 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00 - %% 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 - %% - %% expanded (32 octets): fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 - %% 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50 - - %% PRK = MasterSecret - %% hash = CAPTHash - ExporterInfo = - hexstr2bin("00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73 - 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00 - 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"), - - ExporterMasterSecret = - hexstr2bin("fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 - 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50"), - - ExporterInfo = - tls_v1:create_info(<<"exp master">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)), - - ExporterMasterSecret = - tls_v1:exporter_master_secret(HKDFAlgo, {master_secret, MasterSecret}, CHSF), - - %% {server} derive write traffic keys for application data: - %% - %% PRK (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 50 32 - %% 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43 - %% - %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 - %% - %% key expanded (16 octets): 9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac - %% 92 e3 56 - %% - %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 - %% - %% iv expanded (12 octets): cf 78 2b 88 dd 83 54 9a ad f1 e9 84 - - %% PRK = SAPTrafficsecret - %% key info = WriteKeyInfo - %% iv info = WrtieIVInfo - SWKey = - hexstr2bin("9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac 92 e3 56"), - - SWIV = - hexstr2bin("cf 78 2b 88 dd 83 54 9a ad f1 e9 84"), - - {SWKey, SWIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, SAPTrafficSecret), - - %% {server} derive read traffic keys for handshake data: - %% - %% PRK (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e 2d 8f - %% 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21 - %% - %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 - %% - %% key expanded (16 octets): db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 - %% 25 8d 01 - %% - %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 - %% - %% iv expanded (12 octets): 5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f - - %% PRK = CHSTrafficsecret - %% key info = WriteKeyInfo - %% iv info = WrtieIVInfo - SRKey = - hexstr2bin("db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 25 8d 01"), - - SRIV = - hexstr2bin("5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f"), - - {SRKey, SRIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, CHSTrafficSecret). - - -tls13_finished_verify_data() -> - [{doc,"Test TLS 1.3 Finished message handling"}]. - -tls13_finished_verify_data(_Config) -> - ClientHello = - hexstr2bin("01 00 00 c6 03 03 00 01 02 03 04 05 06 07 08 09 - 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 - 1a 1b 1c 1d 1e 1f 20 e0 e1 e2 e3 e4 e5 e6 e7 e8 - e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 - f9 fa fb fc fd fe ff 00 06 13 01 13 02 13 03 01 - 00 00 77 00 00 00 18 00 16 00 00 13 65 78 61 6d - 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 00 - 0a 00 08 00 06 00 1d 00 17 00 18 00 0d 00 14 00 - 12 04 03 08 04 04 01 05 03 08 05 05 01 08 06 06 - 01 02 01 00 33 00 26 00 24 00 1d 00 20 35 80 72 - d6 36 58 80 d1 ae ea 32 9a df 91 21 38 38 51 ed - 21 a2 8e 3b 75 e9 65 d0 d2 cd 16 62 54 00 2d 00 - 02 01 01 00 2b 00 03 02 03 04"), - - ServerHello = - hexstr2bin("02 00 00 76 03 03 70 71 72 73 74 75 76 77 78 79 - 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 - 8a 8b 8c 8d 8e 8f 20 e0 e1 e2 e3 e4 e5 e6 e7 e8 - e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 - f9 fa fb fc fd fe ff 13 01 00 00 2e 00 33 00 24 - 00 1d 00 20 9f d7 ad 6d cf f4 29 8d d3 f9 6d 5b - 1b 2a f9 10 a0 53 5b 14 88 d7 f8 fa bb 34 9a 98 - 28 80 b6 15 00 2b 00 02 03 04"), - - EncryptedExtensions = - hexstr2bin("08 00 00 02 00 00"), - - Certificate = - hexstr2bin("0b 00 03 2e 00 00 03 2a 00 03 25 30 82 03 21 30 - 82 02 09 a0 03 02 01 02 02 08 15 5a 92 ad c2 04 - 8f 90 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 - 00 30 22 31 0b 30 09 06 03 55 04 06 13 02 55 53 - 31 13 30 11 06 03 55 04 0a 13 0a 45 78 61 6d 70 - 6c 65 20 43 41 30 1e 17 0d 31 38 31 30 30 35 30 - 31 33 38 31 37 5a 17 0d 31 39 31 30 30 35 30 31 - 33 38 31 37 5a 30 2b 31 0b 30 09 06 03 55 04 06 - 13 02 55 53 31 1c 30 1a 06 03 55 04 03 13 13 65 - 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e - 65 74 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d - 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 - 01 01 00 c4 80 36 06 ba e7 47 6b 08 94 04 ec a7 - b6 91 04 3f f7 92 bc 19 ee fb 7d 74 d7 a8 0d 00 - 1e 7b 4b 3a 4a e6 0f e8 c0 71 fc 73 e7 02 4c 0d - bc f4 bd d1 1d 39 6b ba 70 46 4a 13 e9 4a f8 3d - f3 e1 09 59 54 7b c9 55 fb 41 2d a3 76 52 11 e1 - f3 dc 77 6c aa 53 37 6e ca 3a ec be c3 aa b7 3b - 31 d5 6c b6 52 9c 80 98 bc c9 e0 28 18 e2 0b f7 - f8 a0 3a fd 17 04 50 9e ce 79 bd 9f 39 f1 ea 69 - ec 47 97 2e 83 0f b5 ca 95 de 95 a1 e6 04 22 d5 - ee be 52 79 54 a1 e7 bf 8a 86 f6 46 6d 0d 9f 16 - 95 1a 4c f7 a0 46 92 59 5c 13 52 f2 54 9e 5a fb - 4e bf d7 7a 37 95 01 44 e4 c0 26 87 4c 65 3e 40 - 7d 7d 23 07 44 01 f4 84 ff d0 8f 7a 1f a0 52 10 - d1 f4 f0 d5 ce 79 70 29 32 e2 ca be 70 1f df ad - 6b 4b b7 11 01 f4 4b ad 66 6a 11 13 0f e2 ee 82 - 9e 4d 02 9d c9 1c dd 67 16 db b9 06 18 86 ed c1 - ba 94 21 02 03 01 00 01 a3 52 30 50 30 0e 06 03 - 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 03 - 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 03 - 02 06 08 2b 06 01 05 05 07 03 01 30 1f 06 03 55 - 1d 23 04 18 30 16 80 14 89 4f de 5b cc 69 e2 52 - cf 3e a3 00 df b1 97 b8 1d e1 c1 46 30 0d 06 09 - 2a 86 48 86 f7 0d 01 01 0b 05 00 03 82 01 01 00 - 59 16 45 a6 9a 2e 37 79 e4 f6 dd 27 1a ba 1c 0b - fd 6c d7 55 99 b5 e7 c3 6e 53 3e ff 36 59 08 43 - 24 c9 e7 a5 04 07 9d 39 e0 d4 29 87 ff e3 eb dd - 09 c1 cf 1d 91 44 55 87 0b 57 1d d1 9b df 1d 24 - f8 bb 9a 11 fe 80 fd 59 2b a0 39 8c de 11 e2 65 - 1e 61 8c e5 98 fa 96 e5 37 2e ef 3d 24 8a fd e1 - 74 63 eb bf ab b8 e4 d1 ab 50 2a 54 ec 00 64 e9 - 2f 78 19 66 0d 3f 27 cf 20 9e 66 7f ce 5a e2 e4 - ac 99 c7 c9 38 18 f8 b2 51 07 22 df ed 97 f3 2e - 3e 93 49 d4 c6 6c 9e a6 39 6d 74 44 62 a0 6b 42 - c6 d5 ba 68 8e ac 3a 01 7b dd fc 8e 2c fc ad 27 - cb 69 d3 cc dc a2 80 41 44 65 d3 ae 34 8c e0 f3 - 4a b2 fb 9c 61 83 71 31 2b 19 10 41 64 1c 23 7f - 11 a5 d6 5c 84 4f 04 04 84 99 38 71 2b 95 9e d6 - 85 bc 5c 5d d6 45 ed 19 90 94 73 40 29 26 dc b4 - 0e 34 69 a1 59 41 e8 e2 cc a8 4b b6 08 46 36 a0 - 00 00"), - - CertificateVerify = - hexstr2bin("0f 00 01 04 08 04 01 00 17 fe b5 33 ca 6d 00 7d - 00 58 25 79 68 42 4b bc 3a a6 90 9e 9d 49 55 75 - 76 a5 20 e0 4a 5e f0 5f 0e 86 d2 4f f4 3f 8e b8 - 61 ee f5 95 22 8d 70 32 aa 36 0f 71 4e 66 74 13 - 92 6e f4 f8 b5 80 3b 69 e3 55 19 e3 b2 3f 43 73 - df ac 67 87 06 6d cb 47 56 b5 45 60 e0 88 6e 9b - 96 2c 4a d2 8d ab 26 ba d1 ab c2 59 16 b0 9a f2 - 86 53 7f 68 4f 80 8a ef ee 73 04 6c b7 df 0a 84 - fb b5 96 7a ca 13 1f 4b 1c f3 89 79 94 03 a3 0c - 02 d2 9c bd ad b7 25 12 db 9c ec 2e 5e 1d 00 e5 - 0c af cf 6f 21 09 1e bc 4f 25 3c 5e ab 01 a6 79 - ba ea be ed b9 c9 61 8f 66 00 6b 82 44 d6 62 2a - aa 56 88 7c cf c6 6a 0f 38 51 df a1 3a 78 cf f7 - 99 1e 03 cb 2c 3a 0e d8 7d 73 67 36 2e b7 80 5b - 00 b2 52 4f f2 98 a4 da 48 7c ac de af 8a 23 36 - c5 63 1b 3e fa 93 5b b4 11 e7 53 ca 13 b0 15 fe - c7 e4 a7 30 f1 36 9f 9e"), - - BaseKey = - hexstr2bin("a2 06 72 65 e7 f0 65 2a 92 3d 5d 72 ab 04 67 c4 - 61 32 ee b9 68 b6 a3 2d 31 1c 80 58 68 54 88 14"), - - VerifyData = - hexstr2bin("ea 6e e1 76 dc cc 4a f1 85 9e 9e 4e 93 f7 97 ea - c9 a7 8c e4 39 30 1e 35 27 5a d4 3f 3c dd bd e3"), - - Messages = [CertificateVerify, - Certificate, - EncryptedExtensions, - ServerHello, - ClientHello], - - FinishedKey = tls_v1:finished_key(BaseKey, sha256), - VerifyData = tls_v1:finished_verify_data(FinishedKey, sha256, Messages). - - -tls12_ssl_server_tls13_ssl_client() -> - [{doc,"Test basic connection between TLS 1.2 server and TLS 1.3 client"}]. - -tls12_ssl_server_tls13_ssl_client(Config) -> - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - %% Set versions - ServerOpts = [{versions, ['tlsv1.2']}|ServerOpts0], - ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, - {signature_algs_cert, [ecdsa_secp384r1_sha384, - rsa_pss_rsae_sha256, - rsa_pkcs1_sha256, - {sha256,rsa},{sha256,dsa}]}|ClientOpts0], - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ClientOpts}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close_port(Client). - - -tls13_basic_ssl_server_openssl_client() -> - [{doc,"Test TLS 1.3 basic connection between ssl server and openssl s_client"}]. - -tls13_basic_ssl_server_openssl_client(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Set versions - ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], - {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close_port(Client). - -tls13_custom_groups_ssl_server_openssl_client() -> - [{doc,"Test that ssl server can select a common group for key-exchange"}]. - -tls13_custom_groups_ssl_server_openssl_client(Config) -> - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Set versions - ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, - {supported_groups, [x448, secp256r1, secp384r1]}|ServerOpts0], - {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - ClientOpts = [{groups,"P-384:P-256:X25519"}|ClientOpts0], - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close_port(Client). - -tls13_hello_retry_request_ssl_server_openssl_client() -> - [{doc,"Test that ssl server can request a new group when the client's first key share" - "is not supported"}]. - -tls13_hello_retry_request_ssl_server_openssl_client(Config) -> - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Set versions - ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, - {supported_groups, [x448, x25519]}|ServerOpts0], - {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - ClientOpts = [{groups,"P-256:X25519"}|ClientOpts0], - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close_port(Client). - -tls13_client_auth_empty_cert_alert_ssl_server_openssl_client() -> - [{doc,"TLS 1.3: Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to true."}]. - -tls13_client_auth_empty_cert_alert_ssl_server_openssl_client(Config) -> - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), - %% Delete Client Cert and Key - ClientOpts1 = proplists:delete(certfile, ClientOpts0), - ClientOpts = proplists:delete(keyfile, ClientOpts1), - - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Set versions - ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, - {verify, verify_peer}, - {fail_if_no_peer_cert, true}|ServerOpts0], - {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - - ssl_test_lib:check_server_alert(Server, certificate_required), - ssl_test_lib:close(Server), - ssl_test_lib:close_port(Client). - - -tls13_client_auth_empty_cert_ssl_server_openssl_client() -> - [{doc,"TLS 1.3: Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to false."}]. - -tls13_client_auth_empty_cert_ssl_server_openssl_client(Config) -> - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), - %% Delete Client Cert and Key - ClientOpts1 = proplists:delete(certfile, ClientOpts0), - ClientOpts = proplists:delete(keyfile, ClientOpts1), - - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Set versions - ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, - {verify, verify_peer}, - {fail_if_no_peer_cert, false}|ServerOpts0], - {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close_port(Client). - - -tls13_client_auth_ssl_server_openssl_client() -> - [{doc,"TLS 1.3: Test client authentication."}]. - -tls13_client_auth_ssl_server_openssl_client(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Set versions - ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, - {verify, verify_peer}, - {fail_if_no_peer_cert, true}|ServerOpts0], - {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close_port(Client). - - -tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client() -> - [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to true."}]. - -tls13_hrr_client_auth_empty_cert_alert_ssl_server_openssl_client(Config) -> - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), - %% Delete Client Cert and Key - ClientOpts1 = proplists:delete(certfile, ClientOpts0), - ClientOpts2 = proplists:delete(keyfile, ClientOpts1), - ClientOpts = [{groups,"P-256:X25519"}|ClientOpts2], - - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Set versions - ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, - {verify, verify_peer}, - {fail_if_no_peer_cert, true}, - {supported_groups, [x448, x25519]}|ServerOpts0], - {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - - ssl_test_lib:check_server_alert(Server, certificate_required), - ssl_test_lib:close(Server), - ssl_test_lib:close_port(Client). - - -tls13_hrr_client_auth_empty_cert_ssl_server_openssl_client() -> - [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty certificate and fail_if_no_peer_cert is set to false."}]. - -tls13_hrr_client_auth_empty_cert_ssl_server_openssl_client(Config) -> - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), - %% Delete Client Cert and Key - ClientOpts1 = proplists:delete(certfile, ClientOpts0), - ClientOpts2 = proplists:delete(keyfile, ClientOpts1), - ClientOpts = [{groups,"P-256:X25519"}|ClientOpts2], - - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Set versions - ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, - {verify, verify_peer}, - {fail_if_no_peer_cert, false}, - {supported_groups, [x448, x25519]}|ServerOpts0], - {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close_port(Client). - - -tls13_hrr_client_auth_ssl_server_openssl_client() -> - [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication."}]. - -tls13_hrr_client_auth_ssl_server_openssl_client(Config) -> - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), - ClientOpts = [{groups,"P-256:X25519"}|ClientOpts0], - - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Set versions - ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, - {verify, verify_peer}, - {fail_if_no_peer_cert, true}, - {supported_groups, [x448, x25519]}|ServerOpts0], - {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close_port(Client). - - -tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client() -> - [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm"}]. - -tls13_unsupported_sign_algo_client_auth_ssl_server_openssl_client(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Set versions - ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, - {verify, verify_peer}, - %% Skip rsa_pkcs1_sha256! - {signature_algs, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]}, - {fail_if_no_peer_cert, true}|ServerOpts0], - {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - - ssl_test_lib:check_server_alert(Server, insufficient_security), - ssl_test_lib:close(Server), - ssl_test_lib:close_port(Client). - - -%% Triggers Client Alert as openssl s_client does not have a certificate with a -%% signature algorithm supported by the server (signature_algorithms_cert extension -%% of CertificateRequest does not contain the algorithm of the client certificate). -tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client() -> - [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm_cert"}]. - -tls13_unsupported_sign_algo_cert_client_auth_ssl_server_openssl_client(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Set versions - ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, - {log_level, debug}, - {verify, verify_peer}, - {signature_algs, [rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pss_rsae_sha256]}, - %% Skip rsa_pkcs1_sha256! - {signature_algs_cert, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]}, - {fail_if_no_peer_cert, true}|ServerOpts0], - {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - - ssl_test_lib:check_server_alert(Server, certificate_required), - ssl_test_lib:close(Server), - ssl_test_lib:close_port(Client). - - -tls13_connection_information() -> - [{doc,"Test the API function ssl:connection_information/1 in a TLS 1.3 connection"}]. - -tls13_connection_information(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% Set versions - ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ServerOpts0], - {_ClientNode, ServerNode, _Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, connection_information_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_basic_client(openssl, 'tlsv1.3', Port, ClientOpts), - - ssl_test_lib:check_result(Server, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close_port(Client). - - -%%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- send_recv_result(Socket) -> @@ -5768,491 +463,9 @@ tcp_send_recv_result(Socket) -> {ok,"Hello world"} = gen_tcp:recv(Socket, 11), ok. -basic_verify_test_no_close(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ClientOpts}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - {Server, Client}. - -basic_test(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ClientOpts}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -prf_create_plan(TlsVersions, PRFs, Results) -> - lists:foldl(fun(Ver, Acc) -> - A = prf_ciphers_and_expected(Ver, PRFs, Results), - [A|Acc] - end, [], TlsVersions). -prf_ciphers_and_expected(TlsVer, PRFs, Results) -> - case TlsVer of - TlsVer when TlsVer == sslv3 orelse TlsVer == tlsv1 - orelse TlsVer == 'tlsv1.1' orelse TlsVer == 'dtlsv1' -> - Ciphers = ssl:cipher_suites(), - {_, Expected} = lists:keyfind(md5sha, 1, Results), - [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected}, {prf, md5sha}]]; - TlsVer when TlsVer == 'tlsv1.2' orelse TlsVer == 'dtlsv1.2'-> - lists:foldl( - fun(PRF, Acc) -> - Ciphers = prf_get_ciphers(TlsVer, PRF), - case Ciphers of - [] -> - ct:log("No ciphers for PRF algorithm ~p. Skipping.", [PRF]), - Acc; - Ciphers -> - {_, Expected} = lists:keyfind(PRF, 1, Results), - [[{tls_ver, TlsVer}, {ciphers, Ciphers}, {expected, Expected}, - {prf, PRF}] | Acc] - end - end, [], PRFs) - end. -prf_get_ciphers(_, PRF) -> - lists:filter( - fun(C) when tuple_size(C) == 4 andalso - element(4, C) == PRF -> - true; - (_) -> - false - end, - ssl:cipher_suites()). -prf_run_test(_, TlsVer, [], _, Prf) -> - ct:fail({error, cipher_list_empty, TlsVer, Prf}); -prf_run_test(Config, TlsVer, Ciphers, Expected, Prf) -> - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - BaseOpts = [{active, true}, {versions, [TlsVer]}, {ciphers, Ciphers}, {protocol, tls_or_dtls(TlsVer)}], - ServerOpts = BaseOpts ++ proplists:get_value(server_opts, Config), - ClientOpts = BaseOpts ++ proplists:get_value(client_opts, Config), - Server = ssl_test_lib:start_server( - [{node, ServerNode}, {port, 0}, {from, self()}, - {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client( - [{node, ClientNode}, {port, Port}, - {host, Hostname}, {from, self()}, - {mfa, {?MODULE, prf_verify_value, [TlsVer, Expected, Prf]}}, - {options, ClientOpts}]), - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). -prf_verify_value(Socket, TlsVer, Expected, Algo) -> - Ret = ssl:prf(Socket, <<>>, <<>>, [<<>>], 16), - case TlsVer of - sslv3 -> - case Ret of - {error, undefined} -> ok; - _ -> - {error, {expected, {error, undefined}, - got, Ret, tls_ver, TlsVer, prf_algorithm, Algo}} - end; - _ -> - case Ret of - {ok, Expected} -> ok; - {ok, Val} -> {error, {expected, Expected, got, Val, tls_ver, TlsVer, - prf_algorithm, Algo}} - end - end. - -send_recv_result_timeout_client(Socket) -> - {error, timeout} = ssl:recv(Socket, 11, 500), - {error, timeout} = ssl:recv(Socket, 11, 0), - ssl:send(Socket, "Hello world"), - receive - Msg -> - io:format("Msg ~p~n",[Msg]) - after 500 -> - ok - end, - {ok, "Hello world"} = ssl:recv(Socket, 11, 500), - ok. -send_recv_result_timeout_server(Socket) -> - ssl:send(Socket, "Hello"), - {ok, "Hello world"} = ssl:recv(Socket, 11), - ssl:send(Socket, " world"), - ok. - -recv_close(Socket) -> - {error, closed} = ssl:recv(Socket, 11), - receive - {_,{error,closed}} -> - error_extra_close_sent_to_user_process - after 500 -> - ok - end. - - -send_recv_result_active_rizzo(Socket) -> - ssl:send(Socket, "Hello world"), - "Hello world" = ssl_test_lib:active_recv(Socket, 11), - ok. - -send_recv_result_active_no_rizzo(Socket) -> - ssl:send(Socket, "Hello world"), - "Hello world" = ssl_test_lib:active_recv(Socket, 11), - ok. - - -ssl_active_recv(N) -> - ssl_active_recv(N, []). - -ssl_active_recv(0, Acc) -> - Acc; -ssl_active_recv(N, Acc) -> - receive - {ssl, _, Bytes} -> - ssl_active_recv(N-length(Bytes), Acc ++ Bytes) - end. - result_ok(_Socket) -> ok. -renegotiate(Socket, Data) -> - ct:log("Renegotiating ~n", []), - Result = ssl:renegotiate(Socket), - ct:log("Result ~p~n", [Result]), - ssl:send(Socket, Data), - case Result of - ok -> - ok; - Other -> - Other - end. - -renegotiate_reuse_session(Socket, Data) -> - %% Make sure session is registered - ct:sleep(?SLEEP), - renegotiate(Socket, Data). - -renegotiate_immediately(Socket) -> - _ = ssl_test_lib:active_recv(Socket, 11), - ok = ssl:renegotiate(Socket), - {error, renegotiation_rejected} = ssl:renegotiate(Socket), - ct:sleep(?RENEGOTIATION_DISABLE_TIME + ?SLEEP), - ok = ssl:renegotiate(Socket), - ct:log("Renegotiated again"), - ssl:send(Socket, "Hello world"), - ok. - -renegotiate_rejected(Socket) -> - _ = ssl_test_lib:active_recv(Socket, 11), - {error, renegotiation_rejected} = ssl:renegotiate(Socket), - {error, renegotiation_rejected} = ssl:renegotiate(Socket), - ct:sleep(?RENEGOTIATION_DISABLE_TIME +1), - {error, renegotiation_rejected} = ssl:renegotiate(Socket), - ct:log("Failed to renegotiate again"), - ssl:send(Socket, "Hello world"), - ok. - -rizzo_add_mitigation_option(Value, Config) -> - lists:foldl(fun(Opt, Acc) -> - case proplists:get_value(Opt, Acc) of - undefined -> Acc; - C -> - N = lists:keystore(beast_mitigation, 1, C, - {beast_mitigation, Value}), - lists:keystore(Opt, 1, Acc, {Opt, N}) - end - end, Config, - [client_opts, client_dsa_opts, server_opts, server_dsa_opts, - server_ecdsa_opts, server_ecdh_rsa_opts]). - -new_config(PrivDir, ServerOpts0) -> - CaCertFile = proplists:get_value(cacertfile, ServerOpts0), - CertFile = proplists:get_value(certfile, ServerOpts0), - KeyFile = proplists:get_value(keyfile, ServerOpts0), - NewCaCertFile = filename:join(PrivDir, "new_ca.pem"), - NewCertFile = filename:join(PrivDir, "new_cert.pem"), - NewKeyFile = filename:join(PrivDir, "new_key.pem"), - file:copy(CaCertFile, NewCaCertFile), - file:copy(CertFile, NewCertFile), - file:copy(KeyFile, NewKeyFile), - ServerOpts1 = proplists:delete(cacertfile, ServerOpts0), - ServerOpts2 = proplists:delete(certfile, ServerOpts1), - ServerOpts = proplists:delete(keyfile, ServerOpts2), - - {ok, PEM} = file:read_file(NewCaCertFile), - ct:log("CA file content: ~p~n", [public_key:pem_decode(PEM)]), - - [{cacertfile, NewCaCertFile}, {certfile, NewCertFile}, - {keyfile, NewKeyFile} | ServerOpts]. - -session_cache_process(_Type,Config) when is_list(Config) -> - reuse_session(Config). - -init([Type]) -> - ets:new(ssl_test, [named_table, public, set]), - ets:insert(ssl_test, {type, Type}), - case Type of - list -> - spawn(fun() -> session_loop([]) end); - mnesia -> - mnesia:start(), - {atomic,ok} = mnesia:create_table(sess_cache, []), - sess_cache - end. - -session_cb() -> - [{type, Type}] = ets:lookup(ssl_test, type), - Type. - -terminate(Cache) -> - case session_cb() of - list -> - Cache ! terminate; - mnesia -> - catch {atomic,ok} = - mnesia:delete_table(sess_cache) - end. - -lookup(Cache, Key) -> - case session_cb() of - list -> - Cache ! {self(), lookup, Key}, - receive {Cache, Res} -> Res end; - mnesia -> - case mnesia:transaction(fun() -> - mnesia:read(sess_cache, - Key, read) - end) of - {atomic, [{sess_cache, Key, Value}]} -> - Value; - _ -> - undefined - end - end. - -update(Cache, Key, Value) -> - case session_cb() of - list -> - Cache ! {update, Key, Value}; - mnesia -> - {atomic, ok} = - mnesia:transaction(fun() -> - mnesia:write(sess_cache, - {sess_cache, Key, Value}, write) - end) - end. - -delete(Cache, Key) -> - case session_cb() of - list -> - Cache ! {delete, Key}; - mnesia -> - {atomic, ok} = - mnesia:transaction(fun() -> - mnesia:delete(sess_cache, Key) - end) - end. - -foldl(Fun, Acc, Cache) -> - case session_cb() of - list -> - Cache ! {self(),foldl,Fun,Acc}, - receive {Cache, Res} -> Res end; - mnesia -> - Foldl = fun() -> - mnesia:foldl(Fun, Acc, sess_cache) - end, - {atomic, Res} = mnesia:transaction(Foldl), - Res - end. - -select_session(Cache, PartialKey) -> - case session_cb() of - list -> - Cache ! {self(),select_session, PartialKey}, - receive - {Cache, Res} -> - Res - end; - mnesia -> - Sel = fun() -> - mnesia:select(Cache, - [{{sess_cache,{PartialKey,'$1'}, '$2'}, - [],['$$']}]) - end, - {atomic, Res} = mnesia:transaction(Sel), - Res - end. - -session_loop(Sess) -> - receive - terminate -> - ok; - {Pid, lookup, Key} -> - case lists:keysearch(Key,1,Sess) of - {value, {Key,Value}} -> - Pid ! {self(), Value}; - _ -> - Pid ! {self(), undefined} - end, - session_loop(Sess); - {update, Key, Value} -> - NewSess = [{Key,Value}| lists:keydelete(Key,1,Sess)], - session_loop(NewSess); - {delete, Key} -> - session_loop(lists:keydelete(Key,1,Sess)); - {Pid,foldl,Fun,Acc} -> - Res = lists:foldl(Fun, Acc,Sess), - Pid ! {self(), Res}, - session_loop(Sess); - {Pid,select_session,PKey} -> - Sel = fun({{PKey0, Id},Session}, Acc) when PKey == PKey0 -> - [[Id, Session]|Acc]; - (_,Acc) -> - Acc - end, - Sessions = lists:foldl(Sel, [], Sess), - Pid ! {self(), Sessions}, - session_loop(Sess) - end. - - -erlang_ssl_receive(Socket, Data) -> - case ssl_test_lib:active_recv(Socket, length(Data)) of - Data -> - ok; - Other -> - ct:fail({{expected, Data}, {got, Other}}) - end. - -receive_msg(_) -> - receive - Msg -> - Msg - end. - -controlling_process_result(Socket, Pid, Msg) -> - ok = ssl:controlling_process(Socket, Pid), - %% Make sure other side has evaluated controlling_process - %% before message is sent - ct:sleep(?SLEEP), - ssl:send(Socket, Msg), - no_result_msg. - - -controller_dies_result(_Socket, _Pid, _Msg) -> - receive Result -> Result end. - -get_close(Pid, Where) -> - receive - {'EXIT', Pid, _Reason} -> - receive - {_, {ssl_closed, Socket}} -> - ct:log("Socket closed ~p~n",[Socket]); - Unexpected -> - ct:log("Unexpected ~p~n",[Unexpected]), - ct:fail({line, ?LINE-1}) - after 5000 -> - ct:fail({timeout, {line, ?LINE, Where}}) - end; - Unexpected -> - ct:log("Unexpected ~p~n",[Unexpected]), - ct:fail({line, ?LINE-1}) - after 5000 -> - ct:fail({timeout, {line, ?LINE, Where}}) - end. - -run_send_recv_rizzo(Ciphers, Config, Version, Mfa) -> - Result = lists:map(fun(Cipher) -> - rizzo_test(Cipher, Config, Version, Mfa) end, - Ciphers), - case lists:flatten(Result) of - [] -> - ok; - Error -> - ct:log("Cipher suite errors: ~p~n", [Error]), - ct:fail(cipher_suite_failed_see_test_case_log) - end. - -rizzo_test(Cipher, Config, Version, Mfa) -> - {ClientOpts, ServerOpts} = client_server_opts(Cipher, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, Mfa}, - {options, [{active, true}, {ciphers, [Cipher]}, - {versions, [Version]} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, Mfa}, - {options, [{active, true}, {ciphers, [Cipher]}| ClientOpts]}]), - - Result = ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - case Result of - ok -> - []; - Error -> - [{Cipher, Error}] - end. - -client_server_opts(#{key_exchange := KeyAlgo}, Config) - when KeyAlgo == rsa orelse - KeyAlgo == dhe_rsa orelse - KeyAlgo == ecdhe_rsa orelse - KeyAlgo == rsa_psk orelse - KeyAlgo == srp_rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), - ssl_test_lib:ssl_options(server_opts, Config)}; -client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == dss orelse KeyAlgo == dhe_dss -> - {ssl_test_lib:ssl_options(client_dsa_opts, Config), - ssl_test_lib:ssl_options(server_dsa_opts, Config)}; -client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == ecdh_ecdsa orelse KeyAlgo == ecdhe_ecdsa -> - {ssl_test_lib:ssl_options(client_opts, Config), - ssl_test_lib:ssl_options(server_ecdsa_opts, Config)}; -client_server_opts(#{key_exchange := KeyAlgo}, Config) when KeyAlgo == ecdh_rsa -> - {ssl_test_lib:ssl_options(client_opts, Config), - ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)}. - -connection_information_result(Socket) -> - {ok, Info = [_ | _]} = ssl:connection_information(Socket), - case length(Info) > 3 of - true -> - %% Atleast one ssl_option() is set - ct:log("Info ~p", [Info]), - ok; - false -> - ct:fail(no_ssl_options_returned) - end. - -connection_info_result(Socket) -> - {ok, Info} = ssl:connection_information(Socket, [protocol, selected_cipher_suite]), - {ok, {proplists:get_value(protocol, Info), proplists:get_value(selected_cipher_suite, Info)}}. protocol_info_result(Socket) -> {ok, [{protocol, PVersion}]} = ssl:connection_information(Socket, [protocol]), @@ -6262,11 +475,7 @@ version_info_result(Socket) -> {ok, [{version, Version}]} = ssl:connection_information(Socket, [version]), {ok, Version}. -secret_connection_info_result(Socket) -> - {ok, [{client_random, ClientRand}, {server_random, ServerRand}, {master_secret, MasterSecret}]} - = ssl:connection_information(Socket, [client_random, server_random, master_secret]), - is_binary(ClientRand) andalso is_binary(ServerRand) andalso is_binary(MasterSecret). - + connect_dist_s(S) -> Msg = term_to_binary({erlang,term}), ok = ssl:send(S, Msg). @@ -6276,88 +485,11 @@ connect_dist_c(S) -> {ok, Test} = ssl:recv(S, 0, 10000), ok. -tls_downgrade_result(Socket, Pid) -> - ok = ssl_test_lib:send_recv_result(Socket), - Pid ! {self(), ready}, - receive - go -> - ok - end, - case ssl:close(Socket, {self(), 10000}) of - {ok, TCPSocket} -> - inet:setopts(TCPSocket, [{active, true}]), - gen_tcp:send(TCPSocket, "Downgraded"), - receive - {tcp, TCPSocket, <<"Downgraded">>} -> - ok; - {tcp_closed, TCPSocket} -> - ct:fail("Peer timed out, downgrade aborted"), - ok; - Other -> - {error, Other} - end; - {error, timeout} -> - ct:fail("Timed out, downgrade aborted"), - ok; - Fail -> - {error, Fail} - end. - -tls_close(Socket) -> - ok = ssl_test_lib:send_recv_result(Socket), - case ssl:close(Socket, 5000) of - ok -> - ok; - {error, closed} -> - ok; - Other -> - ct:fail(Other) - end. - - %% First two clauses handles 1/n-1 splitting countermeasure Rizzo/Duong-Beast -treashold(N, {3,0}) -> - (N div 2) + 1; -treashold(N, {3,1}) -> - (N div 2) + 1; -treashold(N, _) -> - N + 1. - -get_invalid_inet_option(Socket) -> - {error, {options, {socket_options, foo, _}}} = ssl:getopts(Socket, [foo]), - ok. - -tls_shutdown_result(Socket, server) -> - ssl:send(Socket, "Hej"), - ok = ssl:shutdown(Socket, write), - {ok, "Hej hopp"} = ssl:recv(Socket, 8), - ok; - -tls_shutdown_result(Socket, client) -> - ssl:send(Socket, "Hej hopp"), - ok = ssl:shutdown(Socket, write), - {ok, "Hej"} = ssl:recv(Socket, 3), - ok. - -tls_shutdown_write_result(Socket, server) -> - ct:sleep(?SLEEP), - ssl:shutdown(Socket, write); -tls_shutdown_write_result(Socket, client) -> - ssl:recv(Socket, 0). - dummy(_Socket) -> %% Should not happen as the ssl connection will not be established %% due to fatal handshake failiure exit(kill). -tls_shutdown_both_result(Socket, server) -> - ct:sleep(?SLEEP), - ssl:shutdown(Socket, read_write); -tls_shutdown_both_result(Socket, client) -> - ssl:recv(Socket, 0). - -peername_result(S) -> - ssl:peername(S). - version_option_test(Config, Version) -> ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), @@ -6383,50 +515,3 @@ version_option_test(Config, Version) -> ssl_test_lib:close(Server), ssl_test_lib:close(Client). -try_recv_active(Socket) -> - ssl:send(Socket, "Hello world"), - {error, einval} = ssl:recv(Socket, 11), - ok. -try_recv_active_once(Socket) -> - {error, einval} = ssl:recv(Socket, 11), - ok. - - -wait_for_send(Socket) -> - %% Make sure TLS process processed send message event - _ = ssl:connection_information(Socket). - -tls_or_dtls('dtlsv1') -> - dtls; -tls_or_dtls('dtlsv1.2') -> - dtls; -tls_or_dtls(_) -> - tls. - -hexstr2int(S) -> - B = hexstr2bin(S), - Bits = size(B) * 8, - <<Integer:Bits/integer>> = B, - Integer. - -hexstr2bin(S) when is_binary(S) -> - hexstr2bin(S, <<>>); -hexstr2bin(S) -> - hexstr2bin(list_to_binary(S), <<>>). -%% -hexstr2bin(<<>>, Acc) -> - Acc; -hexstr2bin(<<C,T/binary>>, Acc) when C =:= 32; %% SPACE - C =:= 10; %% LF - C =:= 13 -> %% CR - hexstr2bin(T, Acc); -hexstr2bin(<<X,Y,T/binary>>, Acc) -> - I = hex2int(X) * 16 + hex2int(Y), - hexstr2bin(T, <<Acc/binary,I>>). - -hex2int(C) when $0 =< C, C =< $9 -> - C - $0; -hex2int(C) when $A =< C, C =< $F -> - C - $A + 10; -hex2int(C) when $a =< C, C =< $f -> - C - $a + 10. diff --git a/lib/ssl/test/ssl_bench_SUITE.erl b/lib/ssl/test/ssl_bench_SUITE.erl index 35efa2b8a3..a297539c36 100644 --- a/lib/ssl/test/ssl_bench_SUITE.erl +++ b/lib/ssl/test/ssl_bench_SUITE.erl @@ -174,7 +174,12 @@ do_test(Type, TC, Loop, ParallellConnections, Server) -> end, {TimeInMicro, _} = timer:tc(Run), TotalTests = ParallellConnections * Loop, - TestPerSecond = 1000000 * TotalTests div TimeInMicro, + TestPerSecond = case TimeInMicro of + 0 -> + undefined; + _ -> + 1000000 * TotalTests div TimeInMicro + end, io:format("TC ~p ~p ~p ~p 1/s~n", [TC, Type, ParallellConnections, TestPerSecond]), unlink(SPid), SPid ! quit, diff --git a/lib/ssl/test/ssl_cert_SUITE.erl b/lib/ssl/test/ssl_cert_SUITE.erl new file mode 100644 index 0000000000..fb1695f38a --- /dev/null +++ b/lib/ssl/test/ssl_cert_SUITE.erl @@ -0,0 +1,563 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(ssl_cert_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). +-include_lib("common_test/include/ct.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +all() -> + [ + {group, 'tlsv1.3'}, + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}, + {group, 'dtlsv1.2'}, + {group, 'dtlsv1'} + ]. + +groups() -> + [ + {'tlsv1.3', [], tls_1_3_protocol_groups()}, + {'tlsv1.2', [], pre_tls_1_3_protocol_groups()}, + {'tlsv1.1', [], pre_tls_1_3_protocol_groups()}, + {'tlsv1', [], pre_tls_1_3_protocol_groups()}, + {'sslv3', [], ssl_protocol_groups()}, + {'dtlsv1.2', [], pre_tls_1_3_protocol_groups()}, + {'dtlsv1', [], pre_tls_1_3_protocol_groups()}, + {rsa, [], all_version_tests()}, + {ecdsa, [], all_version_tests()}, + {dsa, [], all_version_tests()}, + {rsa_1_3, [], all_version_tests() ++ tls_1_3_tests() ++ [unsupported_sign_algo_client_auth, + unsupported_sign_algo_cert_client_auth]}, + {ecdsa_1_3, [], all_version_tests() ++ tls_1_3_tests()} + ]. + +ssl_protocol_groups() -> + [{group, rsa}, + {group, dsa}]. + +pre_tls_1_3_protocol_groups() -> + [{group, rsa}, + {group, ecdsa}, + {group, dsa}]. + +tls_1_3_protocol_groups() -> + [{group, rsa_1_3}, + {group, ecdsa_1_3}]. + +tls_1_3_tests() -> + [ + hello_retry_request, + custom_groups, + hello_retry_client_auth, + hello_retry_client_auth_empty_cert_accepted, + hello_retry_client_auth_empty_cert_rejected + ]. + +all_version_tests() -> + [ + no_auth, + auth, + client_auth_empty_cert_accepted, + client_auth_empty_cert_rejected, + client_auth_partial_chain, + client_auth_allow_partial_chain, + client_auth_do_not_allow_partial_chain, + client_auth_partial_chain_fun_fail, + missing_root_cert_no_auth, + missing_root_cert_auth, + missing_root_cert_auth_user_verify_fun_accept, + missing_root_cert_auth_user_verify_fun_reject, + verify_fun_always_run_client, + verify_fun_always_run_server, + incomplete_chain_auth + %%invalid_signature_client + ]. + +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + Config + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:unload(ssl), + application:stop(crypto). + +init_per_group(Group, Config0) when Group == rsa; + Group == rsa_1_3 -> + Config = ssl_test_lib:make_rsa_cert(Config0), + COpts = proplists:get_value(client_rsa_opts, Config), + SOpts = proplists:get_value(server_rsa_opts, Config), + [{cert_key_alg, rsa} | + lists:delete(cert_key_alg, + [{client_cert_opts, COpts}, + {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, + lists:delete(client_cert_opts, Config))])]; +init_per_group(Group, Config0) when Group == ecdsa; + Group == ecdsa_1_3 -> + + PKAlg = crypto:supports(public_keys), + case lists:member(ecdsa, PKAlg) andalso (lists:member(ecdh, PKAlg) orelse lists:member(dh, PKAlg)) of + true -> + Config = ssl_test_lib:make_ecdsa_cert(Config0), + COpts = proplists:get_value(client_ecdsa_opts, Config), + SOpts = proplists:get_value(server_ecdsa_opts, Config), + [{cert_key_alg, ecdsa} | + lists:delete(cert_key_alg, + [{client_cert_opts, COpts}, + {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, + lists:delete(client_cert_opts, Config))] + )]; + false -> + {skip, "Missing EC crypto support"} + end; + +init_per_group(Group, Config0) when Group == dsa -> + PKAlg = crypto:supports(public_keys), + case lists:member(dss, PKAlg) andalso lists:member(dh, PKAlg) of + true -> + Config = ssl_test_lib:make_dsa_cert(Config0), + COpts = proplists:get_value(client_dsa_opts, Config), + SOpts = proplists:get_value(server_dsa_opts, Config), + [{cert_key_alg, dsa} | + lists:delete(cert_key_alg, + [{client_cert_opts, COpts}, + {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, + lists:delete(client_cert_opts, Config))])]; + false -> + {skip, "Missing DSS crypto support"} + end; +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + [{client_type, erlang}, + {server_type, erlang}, {version, GroupName} + | ssl_test_lib:init_tls_version(GroupName, Config)]; + false -> + {skip, "Missing crypto support"} + end; + _ -> + ssl:start(), + Config + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +init_per_testcase(_TestCase, Config) -> + ssl_test_lib:ct_log_supported_protocol_versions(Config), + ct:timetrap({seconds, 10}), + Config. + +end_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +no_auth() -> + ssl_cert_tests:no_auth(). + +no_auth(Config) -> + ssl_cert_tests:no_auth(Config). +%%-------------------------------------------------------------------- +auth() -> + ssl_cert_tests:auth(). +auth(Config) -> + ssl_cert_tests:auth(Config). +%%-------------------------------------------------------------------- +client_auth_empty_cert_accepted() -> + ssl_cert_tests:client_auth_empty_cert_accepted(). +client_auth_empty_cert_accepted(Config) -> + ssl_cert_tests:client_auth_empty_cert_accepted(Config). +%%-------------------------------------------------------------------- +client_auth_empty_cert_rejected() -> + ssl_cert_tests:client_auth_empty_cert_rejected(). +client_auth_empty_cert_rejected(Config) -> + ssl_cert_tests:client_auth_empty_cert_rejected(Config). +%%-------------------------------------------------------------------- +client_auth_partial_chain() -> + ssl_cert_tests:client_auth_partial_chain(). +client_auth_partial_chain(Config) when is_list(Config) -> + ssl_cert_tests:client_auth_partial_chain(Config). + +%%-------------------------------------------------------------------- +client_auth_allow_partial_chain() -> + ssl_cert_tests:client_auth_allow_partial_chain(). +client_auth_allow_partial_chain(Config) when is_list(Config) -> + ssl_cert_tests:client_auth_allow_partial_chain(Config). +%%-------------------------------------------------------------------- +client_auth_do_not_allow_partial_chain() -> + ssl_cert_tests:client_auth_do_not_allow_partial_chain(). +client_auth_do_not_allow_partial_chain(Config) when is_list(Config) -> + ssl_cert_tests:client_auth_do_not_allow_partial_chain(Config). + +%%-------------------------------------------------------------------- +client_auth_partial_chain_fun_fail() -> + ssl_cert_tests:client_auth_partial_chain_fun_fail(). +client_auth_partial_chain_fun_fail(Config) when is_list(Config) -> + ssl_cert_tests:client_auth_partial_chain_fun_fail(Config). + +%%-------------------------------------------------------------------- +missing_root_cert_no_auth() -> + ssl_cert_tests:missing_root_cert_no_auth(). +missing_root_cert_no_auth(Config) when is_list(Config) -> + ssl_cert_tests:missing_root_cert_no_auth(Config). + +%%-------------------------------------------------------------------- +missing_root_cert_auth() -> + [{doc,"Must have ROOT certs to be able to verify verify peer"}]. +missing_root_cert_auth(Config) when is_list(Config) -> + ServerOpts = proplists:delete(cacertfile, ssl_test_lib:ssl_options(server_cert_opts, Config)), + {ClientNode, ServerNode, _} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, [{verify, verify_peer} + | ServerOpts]}]), + + ssl_test_lib:check_result(Server, {error, {options, {cacertfile, ""}}}), + + ClientOpts = proplists:delete(cacertfile, ssl_test_lib:ssl_options(client_cert_opts, Config)), + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, 0}, + {from, self()}, + {options, [{verify, verify_peer} + | ClientOpts]}]), + + ssl_test_lib:check_result(Client, {error, {options, {cacertfile, ""}}}). + +%%-------------------------------------------------------------------- +missing_root_cert_auth_user_verify_fun_accept() -> + [{doc, "Test that the client succeds if the ROOT CA is unknown in verify_peer mode" + " with a verify_fun that accepts the unknown CA error"}]. + +missing_root_cert_auth_user_verify_fun_accept(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config), + FunAndState = {fun(_,{bad_cert, unknown_ca}, UserState) -> + {valid, UserState}; + (_,{bad_cert, _} = Reason, _) -> + {fail, Reason}; + (_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, UserState}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, []}, + ClientOpts = ssl_test_lib:ssl_options([{verify, verify_peer}, + {verify_fun, FunAndState}], Config), + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +%%-------------------------------------------------------------------- +missing_root_cert_auth_user_backwardscompatibility_verify_fun_accept() -> + [{doc, "Test old style verify fun"}]. + +missing_root_cert_auth_user_backwardscompatibility_verify_fun_accept(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config), + AcceptBadCa = fun({bad_cert,unknown_ca}, Acc) -> Acc; + (Other, Acc) -> [Other | Acc] + end, + VerifyFun = + fun(ErrorList) -> + case lists:foldl(AcceptBadCa, [], ErrorList) of + [] -> true; + [_|_] -> false + end + end, + + ClientOpts = ssl_test_lib:ssl_options([{verify, verify_peer}, + {verify_fun, VerifyFun}], Config), + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +%%-------------------------------------------------------------------- +missing_root_cert_auth_user_verify_fun_reject() -> + [{doc, "Test that the client fails if the ROOT CA is unknown in verify_peer mode" + " with a verify_fun that rejects the unknown CA error"}]. + +missing_root_cert_auth_user_verify_fun_reject(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config), + FunAndState = {fun(_,{bad_cert, unknown_ca} = Reason, _UserState) -> + {fail, Reason}; + (_,{bad_cert, _} = Reason, _) -> + {fail, Reason}; + (_,{extension, UserState}, _) -> + {unknown, UserState}; + (_, valid, UserState) -> + {valid, UserState}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, []}, + ClientOpts = ssl_test_lib:ssl_options([{verify, verify_peer}, + {verify_fun, FunAndState}], Config), + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca). +%%-------------------------------------------------------------------- +incomplete_chain_auth() -> + [{doc,"Test that we can verify an incompleat chain when we have the certs to rebuild it"}]. +incomplete_chain_auth(Config) when is_list(Config) -> + DefaultCertConf = ssl_test_lib:default_cert_chain_conf(), + #{client_config := ClientOpts0, + server_config := ServerOpts0} = ssl_test_lib:make_cert_chains_der(proplists:get_value(cert_key_alg, Config), + [{server_chain, DefaultCertConf}, + {client_chain, DefaultCertConf}]), + [ServerRoot| _] = ServerCas = proplists:get_value(cacerts, ServerOpts0), + ClientCas = proplists:get_value(cacerts, ClientOpts0), + ClientOpts = ssl_test_lib:ssl_options([{verify, verify_peer}, + {cacerts, ServerCas ++ ClientCas} | + proplists:delete(cacerts, ClientOpts0)], Config), + ServerOpts = ssl_test_lib:ssl_options([{verify, verify_peer}, + {cacerts, [ServerRoot]} | + proplists:delete(cacerts, ServerOpts0)], Config), + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +%%-------------------------------------------------------------------- +verify_fun_always_run_client() -> + [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}]. + +verify_fun_always_run_client(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + %% If user verify fun is called correctly we fail the connection. + %% otherwise we cannot tell this case apart form where we miss + %% to call users verify fun + FunAndState = {fun(_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, [ChainLen]) -> + {valid, [ChainLen + 1]}; + (_, valid_peer, [1]) -> + {fail, "verify_fun_was_always_run"}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, [0]}, + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, FunAndState} + | ClientOpts]}]), + + ssl_test_lib:check_client_alert(Server, Client, handshake_failure). + +%%-------------------------------------------------------------------- +verify_fun_always_run_server() -> + [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}]. +verify_fun_always_run_server(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_cert_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + %% If user verify fun is called correctly we fail the connection. + %% otherwise we cannot tell this case apart form where we miss + %% to call users verify fun + FunAndState = {fun(_,{extension, _}, UserState) -> + {unknown, UserState}; + (_, valid, [ChainLen]) -> + {valid, [ChainLen + 1]}; + (_, valid_peer, [1]) -> + {fail, "verify_fun_was_always_run"}; + (_, valid_peer, UserState) -> + {valid, UserState} + end, [0]}, + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, + [{verify, verify_peer}, + {verify_fun, FunAndState} | + ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + no_result, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_client_alert(Server, Client, handshake_failure). + +%%-------------------------------------------------------------------- +invalid_signature_client() -> + ssl_cert_tests:invalid_signature_client(). +invalid_signature_client(Config) when is_list(Config) -> + ssl_cert_tests:invalid_signature_client(Config). +%%-------------------------------------------------------------------- +invalid_signature_server() -> + ssl_cert_tests:invalid_signature_client(). +invalid_signature_server(Config) when is_list(Config) -> + ssl_cert_tests:invalid_signature_client(Config). + +%%-------------------------------------------------------------------- +%% TLS 1.3 Test cases ----------------------------------------------- +%%-------------------------------------------------------------------- +hello_retry_request() -> + [{doc,"Test that ssl server can request a new group when the client's first key share" + "is not supported"}]. + +hello_retry_request(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [x448, x25519]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [secp256r1, x25519]}|ClientOpts0], + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). +%%-------------------------------------------------------------------- +custom_groups() -> + [{doc,"Test that ssl server can select a common group for key-exchange"}]. + +custom_groups(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [x448, secp256r1, secp384r1]}|ServerOpts0], + ClientOpts1 = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + ClientOpts = [{supported_groups,[secp384r1, secp256r1, x25519]}|ClientOpts1], + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +%%-------------------------------------------------------------------- +%% Triggers a Server Alert as ssl client does not have a certificate with a +%% signature algorithm supported by the server (signature_algorithms_cert extension +%% of CertificateRequest does not contain the algorithm of the client certificate). +%% ssl client sends an empty certificate. +unsupported_sign_algo_cert_client_auth() -> + [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm_cert"}]. + +unsupported_sign_algo_cert_client_auth(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {signature_algs, [rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pss_rsae_sha256]}, + %% Skip rsa_pkcs1_sha256! + {signature_algs_cert, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]}, + {fail_if_no_peer_cert, true}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required). + +%%-------------------------------------------------------------------- +unsupported_sign_algo_client_auth() -> + [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm"}]. + +unsupported_sign_algo_client_auth(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + %% Skip rsa_pkcs1_sha256! + {signature_algs, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]}, + {fail_if_no_peer_cert, true}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, insufficient_security). +%%-------------------------------------------------------------------- +hello_retry_client_auth() -> + [{doc, "TLS 1.3 (HelloRetryRequest): Test client authentication."}]. + +hello_retry_client_auth(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + ServerOpts1 = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [x448, x25519]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [secp256r1, x25519]}|ClientOpts0], + ServerOpts = [{verify, verify_peer}, + {fail_if_no_peer_cert, true} | ServerOpts1], + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). +%%-------------------------------------------------------------------- +hello_retry_client_auth_empty_cert_accepted() -> + [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty " + "certificate and fail_if_no_peer_cert is set to true."}]. + +hello_retry_client_auth_empty_cert_accepted(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + %% Delete Client Cert and Key + ClientOpts1 = proplists:delete(certfile, ClientOpts0), + ClientOpts2 = proplists:delete(keyfile, ClientOpts1), + + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, false}, + {supported_groups, [x448, x25519]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [secp256r1, x25519]}|ClientOpts2], + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). +%%-------------------------------------------------------------------- +hello_retry_client_auth_empty_cert_rejected() -> + [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client " + "sends an empty certificate and fail_if_no_peer_cert is set to true."}]. + +hello_retry_client_auth_empty_cert_rejected(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + %% Delete Client Cert and Key + ClientOpts1 = proplists:delete(certfile, ClientOpts0), + ClientOpts2 = proplists:delete(keyfile, ClientOpts1), + + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + %% Set versions + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, true}, + {supported_groups, [x448, x25519]}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {supported_groups, [secp256r1, x25519]}|ClientOpts2], + + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required). diff --git a/lib/ssl/test/ssl_cert_tests.erl b/lib/ssl/test/ssl_cert_tests.erl new file mode 100644 index 0000000000..c88daa2185 --- /dev/null +++ b/lib/ssl/test/ssl_cert_tests.erl @@ -0,0 +1,386 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(ssl_cert_tests). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("public_key/include/public_key.hrl"). + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +no_auth() -> + [{doc,"Test connection without authentication"}]. + +no_auth(Config) -> + ClientOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(client_cert_opts, Config)], + ServerOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(server_cert_opts, Config)], + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). +%%-------------------------------------------------------------------- +auth() -> + [{doc,"Test connection with mutual authentication"}]. + +auth(Config) -> + ClientOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(client_cert_opts, Config)], + ServerOpts = [{verify, verify_peer} | ssl_test_lib:ssl_options(server_cert_opts, Config)], + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +%%-------------------------------------------------------------------- +client_auth_empty_cert_accepted() -> + [{doc,"Test client authentication when client sends an empty certificate and " + "fail_if_no_peer_cert is set to false."}]. + +client_auth_empty_cert_accepted(Config) -> + ClientOpts = proplists:delete(keyfile, + proplists:delete(certfile, + ssl_test_lib:ssl_options(client_cert_opts, Config))), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + ServerOpts = [{verify, verify_peer}, + {fail_if_no_peer_cert, false} | ServerOpts0], + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). +%%-------------------------------------------------------------------- +client_auth_empty_cert_rejected() -> + [{doc,"Test client authentication when client sends an empty certificate and " + "fail_if_no_peer_cert is set to true."}]. + +client_auth_empty_cert_rejected(Config) -> + ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} + | ssl_test_lib:ssl_options(server_cert_opts, Config)], + ClientOpts0 = ssl_test_lib:ssl_options([], Config), + %% Delete Client Cert and Key + ClientOpts1 = proplists:delete(certfile, ClientOpts0), + ClientOpts = proplists:delete(keyfile, ClientOpts1), + + Version = proplists:get_value(version,Config), + case Version of + 'tlsv1.3' -> + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required); + _ -> + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, handshake_failure) + end. +%%-------------------------------------------------------------------- +client_auth_partial_chain() -> + [{doc, "Client sends an incompleate chain, by default not acceptable."}]. + +client_auth_partial_chain(Config) when is_list(Config) -> + ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} + | ssl_test_lib:ssl_options(server_cert_opts, Config)], + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts0)), + [{_,RootCA,_} | _] = public_key:pem_decode(ClientCAs), + ClientOpts = [{cacerts, [RootCA]} | + proplists:delete(cacertfile, ClientOpts0)], + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca). + +%%-------------------------------------------------------------------- +client_auth_allow_partial_chain() -> + [{doc, "Server trusts intermediat CA and accepts a partial chain. (partial_chain option)"}]. + +client_auth_allow_partial_chain(Config) when is_list(Config) -> + ServerOpts0 = [{verify, verify_peer}, {fail_if_no_peer_cert, true} + | ssl_test_lib:ssl_options(server_cert_opts, Config)], + ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config), + {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)), + [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ClientCAs), + + PartialChain = fun(CertChain) -> + case lists:member(IntermidiateCA, CertChain) of + true -> + {trusted_ca, IntermidiateCA}; + false -> + unknown_ca + end + end, + ServerOpts = [{cacerts, [IntermidiateCA]}, + {partial_chain, PartialChain} | + proplists:delete(cacertfile, ServerOpts0)], + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + + %%-------------------------------------------------------------------- +client_auth_do_not_allow_partial_chain() -> + [{doc, "Server does not accept the chain sent by the client as ROOT CA is unkown, " + "and we do not choose to trust the intermediate CA. (partial_chain option)"}]. + +client_auth_do_not_allow_partial_chain(Config) when is_list(Config) -> + ServerOpts0 = [{verify, verify_peer}, {fail_if_no_peer_cert, true} + | ssl_test_lib:ssl_options(server_cert_opts, Config)], + ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config), + {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts0)), + [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ServerCAs), + + PartialChain = fun(_CertChain) -> + unknown_ca + end, + ServerOpts = [{cacerts, [IntermidiateCA]}, + {partial_chain, PartialChain} | + proplists:delete(cacertfile, ServerOpts0)], + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca). + + %%-------------------------------------------------------------------- +client_auth_partial_chain_fun_fail() -> + [{doc, "If parial_chain fun crashes, treat it as if it returned unkown_ca"}]. + +client_auth_partial_chain_fun_fail(Config) when is_list(Config) -> + ServerOpts0 = [{verify, verify_peer}, {fail_if_no_peer_cert, true} + | ssl_test_lib:ssl_options(server_cert_opts, Config)], + ClientOpts = ssl_test_lib:ssl_options(client_cert_opts, Config), + + {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts0)), + [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ServerCAs), + + PartialChain = fun(_CertChain) -> + true = false %% crash on purpose + end, + ServerOpts = [{cacerts, [IntermidiateCA]}, + {partial_chain, PartialChain} | + proplists:delete(cacertfile, ServerOpts0)], + + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca). + +%%-------------------------------------------------------------------- +missing_root_cert_no_auth() -> + [{doc,"Test that the client succeds if the ROOT CA is unknown in verify_none mode"}]. + +missing_root_cert_no_auth(Config) -> + ClientOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(client_cert_opts, Config)], + ServerOpts = [{verify, verify_none} | ssl_test_lib:ssl_options(server_cert_opts, Config)], + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +%%-------------------------------------------------------------------- +invalid_signature_client() -> + [{doc,"Test server with invalid signature"}]. + +invalid_signature_client(Config) when is_list(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + PrivDir = proplists:get_value(priv_dir, Config), + + KeyFile = proplists:get_value(keyfile, ClientOpts0), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + ClientCertFile = proplists:get_value(certfile, ClientOpts0), + NewClientCertFile = filename:join(PrivDir, "client_invalid_cert.pem"), + [{'Certificate', ClientDerCert, _}] = ssl_test_lib:pem_to_der(ClientCertFile), + ClientOTPCert = public_key:pkix_decode_cert(ClientDerCert, otp), + ClientOTPTbsCert = ClientOTPCert#'OTPCertificate'.tbsCertificate, + NewClientDerCert = public_key:pkix_sign(ClientOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewClientCertFile, [{'Certificate', NewClientDerCert, not_encrypted}]), + ClientOpts = [{certfile, NewClientCertFile} | proplists:delete(certfile, ClientOpts0)], + ServerOpts = [{verify, verify_peer} | ServerOpts0], + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca). + +%%-------------------------------------------------------------------- +invalid_signature_server() -> + [{doc,"Test client with invalid signature"}]. + +invalid_signature_server(Config) when is_list(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + PrivDir = proplists:get_value(priv_dir, Config), + + KeyFile = proplists:get_value(keyfile, ServerOpts0), + [KeyEntry] = ssl_test_lib:pem_to_der(KeyFile), + Key = ssl_test_lib:public_key(public_key:pem_entry_decode(KeyEntry)), + + ServerCertFile = proplists:get_value(certfile, ServerOpts0), + NewServerCertFile = filename:join(PrivDir, "server_invalid_cert.pem"), + [{'Certificate', ServerDerCert, _}] = ssl_test_lib:pem_to_der(ServerCertFile), + ServerOTPCert = public_key:pkix_decode_cert(ServerDerCert, otp), + ServerOTPTbsCert = ServerOTPCert#'OTPCertificate'.tbsCertificate, + NewServerDerCert = public_key:pkix_sign(ServerOTPTbsCert, Key), + ssl_test_lib:der_to_pem(NewServerCertFile, [{'Certificate', NewServerDerCert, not_encrypted}]), + ServerOpts = [{certfile, NewServerCertFile} | proplists:delete(certfile, ServerOpts0)], + ClientOpts = [{verify, verify_peer} | ClientOpts0], + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, unknown_ca). + +%%-------------------------------------------------------------------- +%% TLS 1.3 Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +hello_retry_request() -> + [{doc,"Test that ssl server can request a new group when the client's first key share" + "is not supported"}]. + +hello_retry_request(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + + {ServerOpts, ClientOpts} = group_config(Config, + [{versions, ['tlsv1.2','tlsv1.3']} | ServerOpts0], + [{versions, ['tlsv1.2','tlsv1.3']} | ClientOpts0]), + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). +%%-------------------------------------------------------------------- +custom_groups() -> + [{doc,"Test that ssl server can select a common group for key-exchange"}]. + +custom_groups(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + + {ServerOpts, ClientOpts} = group_config_custom(Config, + [{versions, ['tlsv1.2','tlsv1.3']} | ServerOpts0], + [{versions, ['tlsv1.2','tlsv1.3']} | ClientOpts0]), + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +%%-------------------------------------------------------------------- +%% Triggers a Server Alert as ssl client does not have a certificate with a +%% signature algorithm supported by the server (signature_algorithms_cert extension +%% of CertificateRequest does not contain the algorithm of the client certificate). +%% ssl client sends an empty certificate. +unsupported_sign_algo_cert_client_auth() -> + [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm_cert"}]. + +unsupported_sign_algo_cert_client_auth(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {signature_algs, [rsa_pkcs1_sha256, rsa_pkcs1_sha384, rsa_pss_rsae_sha256]}, + %% Skip rsa_pkcs1_sha256! + {signature_algs_cert, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]}, + {fail_if_no_peer_cert, true}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required). +%%-------------------------------------------------------------------- +unsupported_sign_algo_client_auth() -> + [{doc,"TLS 1.3: Test client authentication with unsupported signature_algorithm"}]. + +unsupported_sign_algo_client_auth(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + ServerOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + %% Skip rsa_pkcs1_sha256! + {signature_algs, [rsa_pkcs1_sha384, rsa_pkcs1_sha512]}, + {fail_if_no_peer_cert, true}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}|ClientOpts0], + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, insufficient_security). +%%-------------------------------------------------------------------- +hello_retry_client_auth() -> + [{doc, "TLS 1.3 (HelloRetryRequest): Test client authentication."}]. + +hello_retry_client_auth(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + + {ServerOpts, ClientOpts} = group_config(Config, + [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, true} | ServerOpts0], + [{versions, ['tlsv1.2','tlsv1.3']}, {verify, verify_peer} | ClientOpts0]), + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). +%%-------------------------------------------------------------------- +hello_retry_client_auth_empty_cert_accepted() -> + [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client sends an empty " + "certificate and fail_if_no_peer_cert is set to false."}]. + +hello_retry_client_auth_empty_cert_accepted(Config) -> + ClientOpts0 = proplists:delete(keyfile, + proplists:delete(certfile, + ssl_test_lib:ssl_options(client_cert_opts, Config))), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + + {ServerOpts, ClientOpts} = group_config(Config, + [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, false} | ServerOpts0], + [{versions, ['tlsv1.2','tlsv1.3']}, {verify, verify_peer} | ClientOpts0]), + + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). +%%-------------------------------------------------------------------- +hello_retry_client_auth_empty_cert_rejected() -> + [{doc,"TLS 1.3 (HelloRetryRequest): Test client authentication when client " + "sends an empty certificate and fail_if_no_peer_cert is set to true."}]. + +hello_retry_client_auth_empty_cert_rejected(Config) -> + ClientOpts0 = proplists:delete(keyfile, + proplists:delete(certfile, + ssl_test_lib:ssl_options(client_cert_opts, Config))), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + + {ServerOpts, ClientOpts} = group_config(Config, + [{versions, ['tlsv1.2','tlsv1.3']}, + {verify, verify_peer}, + {fail_if_no_peer_cert, true} | ServerOpts0], + [{versions, ['tlsv1.2','tlsv1.3']}, {verify, verify_peer} | ClientOpts0]), + + ssl_test_lib:basic_alert(ClientOpts, ServerOpts, Config, certificate_required). + + +%%-------------------------------------------------------------------- +%% Internal functions ----------------------------------------------- +%%-------------------------------------------------------------------- + +group_config_custom(Config, ServerOpts, ClientOpts) -> + case proplists:get_value(client_type, Config) of + erlang -> + {[{groups,"X448:P-256:P-384"} | ServerOpts], + [{supported_groups, [secp384r1, secp256r1, x25519]} | ClientOpts]}; + openssl -> + {[{supported_groups, [x448, secp256r1, secp384r1]} | ServerOpts], + [{groups,"P-384:P-256:X25519"} | ClientOpts]} + end. + +group_config(Config, ServerOpts, ClientOpts) -> + case proplists:get_value(client_type, Config) of + erlang -> + {[{groups,"X448:X25519"} | ServerOpts], + [{supported_groups, [secp256r1, x25519]} | ClientOpts]}; + openssl -> + {[{supported_groups, [x448, x25519]} | ServerOpts], + [{groups,"P-256:X25519"} | ClientOpts]} + end. + +test_ciphers(_, 'tlsv1.3' = Version) -> + Ciphers = ssl:cipher_suites(default, Version), + ct:log("Version ~p Testing ~p~n", [Version, Ciphers]), + OpenSSLCiphers = openssl_ciphers(), + ct:log("OpenSSLCiphers ~p~n", [OpenSSLCiphers]), + lists:filter(fun(C) -> + ct:log("Cipher ~p~n", [C]), + lists:member(ssl_cipher_format:suite_map_to_openssl_str(C), OpenSSLCiphers) + end, Ciphers); +test_ciphers(Kex, Version) -> + Ciphers = ssl:filter_cipher_suites(ssl:cipher_suites(default, Version), + [{key_exchange, Kex}]), + ct:log("Version ~p Testing ~p~n", [Version, Ciphers]), + OpenSSLCiphers = openssl_ciphers(), + ct:log("OpenSSLCiphers ~p~n", [OpenSSLCiphers]), + lists:filter(fun(C) -> + ct:log("Cipher ~p~n", [C]), + lists:member(ssl_cipher_format:suite_map_to_openssl_str(C), OpenSSLCiphers) + end, Ciphers). + + + +openssl_ciphers() -> + Str = os:cmd("openssl ciphers"), + string:split(string:strip(Str, right, $\n), ":", all). diff --git a/lib/ssl/test/ssl_certificate_verify_SUITE.erl b/lib/ssl/test/ssl_certificate_verify_SUITE.erl index 5431cda5af..f38858e0bf 100644 --- a/lib/ssl/test/ssl_certificate_verify_SUITE.erl +++ b/lib/ssl/test/ssl_certificate_verify_SUITE.erl @@ -40,6 +40,7 @@ %%-------------------------------------------------------------------- all() -> [ + {group, 'tlsv1.3'}, {group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, @@ -50,6 +51,7 @@ all() -> groups() -> [ + {'tlsv1.3', [], all_protocol_groups()}, {'tlsv1.2', [], all_protocol_groups()}, {'tlsv1.1', [], all_protocol_groups()}, {'tlsv1', [], all_protocol_groups()}, @@ -69,37 +71,20 @@ all_protocol_groups() -> {group, error_handling}]. tests() -> - [verify_peer, - verify_none, - server_require_peer_cert_ok, - server_require_peer_cert_fail, - server_require_peer_cert_empty_ok, - server_require_peer_cert_partial_chain, - server_require_peer_cert_allow_partial_chain, - server_require_peer_cert_do_not_allow_partial_chain, - server_require_peer_cert_partial_chain_fun_fail, - verify_fun_always_run_client, - verify_fun_always_run_server, - cert_expired, - invalid_signature_client, - invalid_signature_server, + [cert_expired, + %invalid_signature_client, + %%invalid_signature_server, extended_key_usage_verify_both, extended_key_usage_verify_server, critical_extension_verify_client, critical_extension_verify_server, critical_extension_verify_none, - customize_hostname_check, - incomplete_chain, long_chain ]. error_handling_tests()-> [client_with_cert_cipher_suites_handshake, - server_verify_no_cacerts, - unknown_server_ca_fail, - unknown_server_ca_accept_verify_none, - unknown_server_ca_accept_verify_peer, - unknown_server_ca_accept_backwardscompatibility, + %%unknown_server_ca_accept_backwardscompatibility, no_authority_key_identifier, no_authority_key_identifier_keyEncipherment]. @@ -158,61 +143,6 @@ end_per_testcase(_TestCase, Config) -> %%-------------------------------------------------------------------- %% Test Cases -------------------------------------------------------- %%-------------------------------------------------------------------- - -verify_peer() -> - [{doc,"Test option verify_peer"}]. -verify_peer(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - Active = proplists:get_value(active, Config), - ReceiveFunction = proplists:get_value(receive_function, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active}, {verify, verify_peer} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active}, {verify, verify_peer} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -verify_none() -> - [{doc,"Test option verify_none"}]. - -verify_none(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - Active = proplists:get_value(active, Config), - ReceiveFunction = proplists:get_value(receive_function, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active}, {verify, verify_none} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active}, - {verify, verify_none} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - server_verify_client_once() -> [{doc,"Test server option verify_client_once"}]. @@ -251,303 +181,6 @@ server_verify_client_once(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -server_require_peer_cert_ok() -> - [{doc,"Test server option fail_if_no_peer_cert when peer sends cert"}]. - -server_require_peer_cert_ok(Config) when is_list(Config) -> - ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} - | ssl_test_lib:ssl_options(server_rsa_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - Active = proplists:get_value(active, Config), - ReceiveFunction = proplists:get_value(receive_function, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -server_require_peer_cert_fail() -> - [{doc,"Test server option fail_if_no_peer_cert when peer doesn't send cert"}]. - -server_require_peer_cert_fail(Config) when is_list(Config) -> - ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} - | ssl_test_lib:ssl_options(server_rsa_opts, Config)], - BadClientOpts = ssl_test_lib:ssl_options(empty_client_opts, Config), - Active = proplists:get_value(active, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, [{active, Active} | ServerOpts]}]), - - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {options, [{active, Active} | BadClientOpts]}]), - - ssl_test_lib:check_server_alert(Server, Client, handshake_failure). - -%%-------------------------------------------------------------------- -server_require_peer_cert_empty_ok() -> - [{doc,"Test server option fail_if_no_peer_cert when peer sends cert"}]. - -server_require_peer_cert_empty_ok(Config) when is_list(Config) -> - ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, false} - | ssl_test_lib:ssl_options(server_rsa_opts, Config)], - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), - Active = proplists:get_value(active, Config), - ReceiveFunction = proplists:get_value(receive_function, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - ClientOpts = proplists:delete(keyfile, proplists:delete(certfile, ClientOpts0)), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -server_require_peer_cert_partial_chain() -> - [{doc, "Client sends an incompleate chain, by default not acceptable."}]. - -server_require_peer_cert_partial_chain(Config) when is_list(Config) -> - ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} - | ssl_test_lib:ssl_options(server_rsa_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - Active = proplists:get_value(active, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)), - [{_,RootCA,_} | _] = public_key:pem_decode(ClientCAs), - - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{active, Active} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{active, Active}, - {cacerts, [RootCA]} | - proplists:delete(cacertfile, ClientOpts)]}]), - ssl_test_lib:check_server_alert(Server, Client, unknown_ca). - -%%-------------------------------------------------------------------- -server_require_peer_cert_allow_partial_chain() -> - [{doc, "Server trusts intermediat CA and accepts a partial chain. (partial_chain option)"}]. - -server_require_peer_cert_allow_partial_chain(Config) when is_list(Config) -> - ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} - | ssl_test_lib:ssl_options(server_rsa_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Active = proplists:get_value(active, Config), - ReceiveFunction = proplists:get_value(receive_function, Config), - - {ok, ClientCAs} = file:read_file(proplists:get_value(cacertfile, ClientOpts)), - [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ClientCAs), - - PartialChain = fun(CertChain) -> - case lists:member(IntermidiateCA, CertChain) of - true -> - {trusted_ca, IntermidiateCA}; - false -> - unknown_ca - end - end, - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active}, - {cacerts, [IntermidiateCA]}, - {partial_chain, PartialChain} | - proplists:delete(cacertfile, ServerOpts)]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active} | ClientOpts]}]), - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - - %%-------------------------------------------------------------------- -server_require_peer_cert_do_not_allow_partial_chain() -> - [{doc, "Server does not accept the chain sent by the client as ROOT CA is unkown, " - "and we do not choose to trust the intermediate CA. (partial_chain option)"}]. - -server_require_peer_cert_do_not_allow_partial_chain(Config) when is_list(Config) -> - ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} - | ssl_test_lib:ssl_options(server_rsa_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)), - [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ServerCAs), - - PartialChain = fun(_CertChain) -> - unknown_ca - end, - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{cacerts, [IntermidiateCA]}, - {partial_chain, PartialChain} | - proplists:delete(cacertfile, ServerOpts)]}]), - - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ClientOpts}]), - ssl_test_lib:check_server_alert(Server, Client, unknown_ca). - %%-------------------------------------------------------------------- -server_require_peer_cert_partial_chain_fun_fail() -> - [{doc, "If parial_chain fun crashes, treat it as if it returned unkown_ca"}]. - -server_require_peer_cert_partial_chain_fun_fail(Config) when is_list(Config) -> - ServerOpts = [{verify, verify_peer}, {fail_if_no_peer_cert, true} - | ssl_test_lib:ssl_options(server_rsa_opts, Config)], - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - {ok, ServerCAs} = file:read_file(proplists:get_value(cacertfile, ServerOpts)), - [{_,_,_}, {_, IntermidiateCA, _} | _] = public_key:pem_decode(ServerCAs), - - PartialChain = fun(_CertChain) -> - true = false %% crash on purpose - end, - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, [{cacerts, [IntermidiateCA]}, - {partial_chain, PartialChain} | - proplists:delete(cacertfile, ServerOpts)]}]), - - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ClientOpts}]), - ssl_test_lib:check_server_alert(Server, Client, unknown_ca). - -%%-------------------------------------------------------------------- -verify_fun_always_run_client() -> - [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}]. - -verify_fun_always_run_client(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - %% If user verify fun is called correctly we fail the connection. - %% otherwise we cannot tell this case apart form where we miss - %% to call users verify fun - FunAndState = {fun(_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, [ChainLen]) -> - {valid, [ChainLen + 1]}; - (_, valid_peer, [1]) -> - {fail, "verify_fun_was_always_run"}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, [0]}, - - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, FunAndState} - | ClientOpts]}]), - - ssl_test_lib:check_client_alert(Server, Client, handshake_failure). - -%%-------------------------------------------------------------------- -verify_fun_always_run_server() -> - [{doc,"Verify that user verify_fun is always run (for valid and valid_peer not only unknown_extension)"}]. -verify_fun_always_run_server(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - %% If user verify fun is called correctly we fail the connection. - %% otherwise we cannot tell this case apart form where we miss - %% to call users verify fun - FunAndState = {fun(_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, [ChainLen]) -> - {valid, [ChainLen + 1]}; - (_, valid_peer, [1]) -> - {fail, "verify_fun_was_always_run"}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, [0]}, - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, FunAndState} | - ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, ClientOpts}]), - - ssl_test_lib:check_client_alert(Server, Client, handshake_failure). -%%-------------------------------------------------------------------- - cert_expired() -> [{doc,"Test server with expired certificate"}]. @@ -854,6 +487,7 @@ invalid_signature_server(Config) when is_list(Config) -> {from, self()}, {options, [{verify, verify_peer} | ClientOpts]}]), ssl_test_lib:check_server_alert(Server, Client, unknown_ca). + %%-------------------------------------------------------------------- invalid_signature_client() -> @@ -928,235 +562,6 @@ client_with_cert_cipher_suites_handshake(Config) when is_list(Config) -> %%-------------------------------------------------------------------- -server_verify_no_cacerts() -> - [{doc,"Test server must have cacerts if it wants to verify client"}]. -server_verify_no_cacerts(Config) when is_list(Config) -> - ServerOpts = proplists:delete(cacertfile, ssl_test_lib:ssl_options(server_rsa_opts, Config)), - {_, ServerNode, _} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, [{verify, verify_peer} - | ServerOpts]}]), - - ssl_test_lib:check_result(Server, {error, {options, {cacertfile, ""}}}). - - -%%-------------------------------------------------------------------- -unknown_server_ca_fail() -> - [{doc,"Test that the client fails if the ca is unknown in verify_peer mode"}]. -unknown_server_ca_fail(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(empty_client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - FunAndState = {fun(_,{bad_cert, unknown_ca} = Reason, _) -> - {fail, Reason}; - (_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, UserState) -> - {valid, [test_to_update_user_state | UserState]}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, []}, - - Client = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - no_result, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, FunAndState} - | ClientOpts]}]), - ssl_test_lib:check_client_alert(Server, Client, unknown_ca). - -%%-------------------------------------------------------------------- -unknown_server_ca_accept_verify_none() -> - [{doc,"Test that the client succeds if the ca is unknown in verify_none mode"}]. -unknown_server_ca_accept_verify_none(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(empty_client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - send_recv_result_active, []}}, - {options, - [{verify, verify_none}| ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). -%%-------------------------------------------------------------------- -unknown_server_ca_accept_verify_peer() -> - [{doc, "Test that the client succeds if the ca is unknown in verify_peer mode" - " with a verify_fun that accepts the unknown ca error"}]. -unknown_server_ca_accept_verify_peer(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(empty_client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - FunAndState = {fun(_,{bad_cert, unknown_ca}, UserState) -> - {valid, UserState}; - (_,{bad_cert, _} = Reason, _) -> - {fail, Reason}; - (_,{extension, _}, UserState) -> - {unknown, UserState}; - (_, valid, UserState) -> - {valid, UserState}; - (_, valid_peer, UserState) -> - {valid, UserState} - end, []}, - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - send_recv_result_active, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, FunAndState}| ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- -unknown_server_ca_accept_backwardscompatibility() -> - [{doc,"Test that old style verify_funs will work"}]. -unknown_server_ca_accept_backwardscompatibility(Config) when is_list(Config) -> - ClientOpts = ssl_test_lib:ssl_options(empty_client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - AcceptBadCa = fun({bad_cert,unknown_ca}, Acc) -> Acc; - (Other, Acc) -> [Other | Acc] - end, - VerifyFun = - fun(ErrorList) -> - case lists:foldl(AcceptBadCa, [], ErrorList) of - [] -> true; - [_|_] -> false - end - end, - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - send_recv_result_active, []}}, - {options, - [{verify, verify_peer}, - {verify_fun, VerifyFun}| ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). - -%%-------------------------------------------------------------------- - -customize_hostname_check() -> - [{doc,"Test option customize_hostname_check."}]. -customize_hostname_check(Config) when is_list(Config) -> - Ext = [#'Extension'{extnID = ?'id-ce-subjectAltName', - extnValue = [{dNSName, "*.example.org"}], - critical = false} - ], - {ClientOpts0, ServerOpts0} = ssl_test_lib:make_rsa_cert_chains([{server_chain, - [[], - [], - [{extensions, Ext}] - ]}], - Config, "https_hostname_convention"), - ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), - ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config), - - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - CustomFun = public_key:pkix_verify_hostname_match_fun(https), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, send_recv_result_active, []}}, - {options, - [{server_name_indication, "other.example.org"}, - {customize_hostname_check, - [{match_fun, CustomFun}]} | ClientOpts] - }]), - ssl_test_lib:check_result(Server, ok, Client, ok), - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - - Client1 = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, ClientOpts} - ]), - ssl_test_lib:check_client_alert(Server, Client1, handshake_failure). - -incomplete_chain() -> - [{doc,"Test option verify_peer"}]. -incomplete_chain(Config) when is_list(Config) -> - DefConf = ssl_test_lib:default_cert_chain_conf(), - CertChainConf = ssl_test_lib:gen_conf(rsa, rsa, DefConf, DefConf), - #{server_config := ServerConf, - client_config := ClientConf} = public_key:pkix_test_data(CertChainConf), - [ServerRoot| _] = ServerCas = proplists:get_value(cacerts, ServerConf), - ClientCas = proplists:get_value(cacerts, ClientConf), - - Active = proplists:get_value(active, Config), - ReceiveFunction = proplists:get_value(receive_function, Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active}, {verify, verify_peer}, - {cacerts, [ServerRoot]} | - proplists:delete(cacerts, ServerConf)]}]), - Port = ssl_test_lib:inet_port(Server), - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, ReceiveFunction, []}}, - {options, [{active, Active}, - {verify, verify_peer}, - {cacerts, ServerCas ++ ClientCas} | - proplists:delete(cacerts, ClientConf)]}]), - ssl_test_lib:check_result(Server, ok, Client, ok), - ssl_test_lib:close(Server), - ssl_test_lib:close(Client). long_chain() -> [{doc,"Test option verify_peer"}]. diff --git a/lib/ssl/test/ssl_cipher_suite_SUITE.erl b/lib/ssl/test/ssl_cipher_suite_SUITE.erl index 51788c29e7..e598d662e9 100644 --- a/lib/ssl/test/ssl_cipher_suite_SUITE.erl +++ b/lib/ssl/test/ssl_cipher_suite_SUITE.erl @@ -45,7 +45,7 @@ groups() -> {'tlsv1.2', [], kex()}, {'tlsv1.1', [], kex()}, {'tlsv1', [], kex()}, - {'sslv3', [], kex()}, + {'sslv3', [], ssl3_kex()}, {'dtlsv1.2', [], kex()}, {'dtlsv1', [], kex()}, {dhe_rsa, [],[dhe_rsa_3des_ede_cbc, @@ -130,6 +130,11 @@ groups() -> kex() -> rsa() ++ ecdsa() ++ dss() ++ anonymous(). + +ssl3_kex() -> + ssl3_rsa() ++ ssl3_dss() ++ ssl3_anonymous(). + + rsa() -> [{group, dhe_rsa}, {group, ecdhe_rsa}, @@ -138,6 +143,11 @@ rsa() -> {group, rsa_psk} ]. +ssl3_rsa() -> + [{group, dhe_rsa}, + {group, rsa} + ]. + ecdsa() -> [{group, ecdhe_ecdsa}]. @@ -145,6 +155,10 @@ dss() -> [{group, dhe_dss}, {group, srp_dss}]. +ssl3_dss() -> + [{group, dhe_dss} + ]. + anonymous() -> [{group, dh_anon}, {group, ecdh_anon}, @@ -154,6 +168,10 @@ anonymous() -> {group, srp_anon} ]. +ssl3_anonymous() -> + [{group, dh_anon}]. + + init_per_suite(Config) -> catch crypto:stop(), try crypto:start() of diff --git a/lib/ssl/test/ssl_dist_SUITE.erl b/lib/ssl/test/ssl_dist_SUITE.erl index 003e1fc448..7cfb2ac0c5 100644 --- a/lib/ssl/test/ssl_dist_SUITE.erl +++ b/lib/ssl/test/ssl_dist_SUITE.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2007-2018. All Rights Reserved. +%% Copyright Ericsson AB 2007-2019. 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. @@ -311,9 +311,11 @@ listen_port_options(Config) when is_list(Config) -> catch _:Reason -> stop_ssl_node(NH2), + stop_ssl_node(NH1), ct:fail(Reason) end, stop_ssl_node(NH2), + stop_ssl_node(NH1), success(Config). %%-------------------------------------------------------------------- diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index 1b432970b6..2750a4a9dc 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -214,7 +214,7 @@ encode_decode_srp(_Config) -> 0,3, % HostNameLength 98,97,114>>, % hostname = "bar" EncodedExts0 = <<?UINT16(_),EncodedExts/binary>> = - ssl_handshake:encode_hello_extensions(Exts), + ssl_handshake:encode_hello_extensions(Exts, {3,3}), Exts = ssl_handshake:decode_hello_extensions(EncodedExts, {3,3}, {3,3}, client). signature_algorithms(Config) -> diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_SUITE.erl index 878e983bb9..8a76a3a82b 100644 --- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_npn_SUITE.erl @@ -19,7 +19,7 @@ %% %% --module(ssl_npn_handshake_SUITE). +-module(ssl_npn_SUITE). %% Note: This directive should only be used in test suites. -compile(export_all). diff --git a/lib/ssl/test/ssl_npn_hello_SUITE.erl b/lib/ssl/test/ssl_npn_hello_SUITE.erl index 46734ba180..7dbc0c5134 100644 --- a/lib/ssl/test/ssl_npn_hello_SUITE.erl +++ b/lib/ssl/test/ssl_npn_hello_SUITE.erl @@ -24,10 +24,11 @@ %% Note: This directive should only be used in test suites. -compile(export_all). --include("ssl_cipher.hrl"). --include("ssl_internal.hrl"). --include("tls_handshake.hrl"). --include("tls_record.hrl"). + +-include_lib("ssl/src/tls_record.hrl"). +-include_lib("ssl/src/tls_handshake.hrl"). +-include_lib("ssl/src/ssl_cipher.hrl"). +-include_lib("ssl/src/ssl_internal.hrl"). -include_lib("common_test/include/ct.hrl"). %%-------------------------------------------------------------------- diff --git a/lib/ssl/test/ssl_pem_cache_SUITE.erl b/lib/ssl/test/ssl_pem_cache_SUITE.erl index 6f11e2bbe8..3c7f6ab20f 100644 --- a/lib/ssl/test/ssl_pem_cache_SUITE.erl +++ b/lib/ssl/test/ssl_pem_cache_SUITE.erl @@ -34,7 +34,10 @@ %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- all() -> - [pem_cleanup, invalid_insert]. + [ + pem_cleanup, + clear_pem_cache, + invalid_insert]. groups() -> []. @@ -110,6 +113,37 @@ pem_cleanup(Config)when is_list(Config) -> ssl_test_lib:close(Client), false = Size == Size1. +clear_pem_cache() -> + [{doc,"Test that internal reference tabel is cleaned properly even when " + " the PEM cache is cleared" }]. +clear_pem_cache(Config) when is_list(Config) -> + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + [_,{FilRefDb, _} |_] = element(6, State), + {Server, Client} = basic_verify_test_no_close(Config), + CountReferencedFiles = fun({_, -1}, Acc) -> + Acc; + ({_, N}, Acc) -> + N + Acc + end, + + 2 = ets:foldl(CountReferencedFiles, 0, FilRefDb), + ssl:clear_pem_cache(), + _ = sys:get_status(whereis(ssl_manager)), + {Server1, Client1} = basic_verify_test_no_close(Config), + 4 = ets:foldl(CountReferencedFiles, 0, FilRefDb), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + ct:sleep(2000), + _ = sys:get_status(whereis(ssl_manager)), + 2 = ets:foldl(CountReferencedFiles, 0, FilRefDb), + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1), + ct:sleep(2000), + _ = sys:get_status(whereis(ssl_manager)), + 0 = ets:foldl(CountReferencedFiles, 0, FilRefDb). + invalid_insert() -> [{doc, "Test that insert of invalid pem does not cause empty cache entry"}]. invalid_insert(Config)when is_list(Config) -> @@ -163,3 +197,22 @@ later()-> Gregorian = calendar:datetime_to_gregorian_seconds(DateTime), calendar:gregorian_seconds_to_datetime(Gregorian + (2 * ?CLEANUP_INTERVAL)). +basic_verify_test_no_close(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + {Server, Client}. diff --git a/lib/ssl/test/ssl_renegotiate_SUITE.erl b/lib/ssl/test/ssl_renegotiate_SUITE.erl new file mode 100644 index 0000000000..ef3f9ebb52 --- /dev/null +++ b/lib/ssl/test/ssl_renegotiate_SUITE.erl @@ -0,0 +1,499 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(ssl_renegotiate_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +-define(SLEEP, 500). +-define(RENEGOTIATION_DISABLE_TIME, 12000). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +all() -> + [ + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}, + {group, 'dtlsv1.2'}, + {group, 'dtlsv1'} + ]. + +groups() -> + [{'dtlsv1.2', [], renegotiate_tests()}, + {'dtlsv1', [], renegotiate_tests()}, + {'tlsv1.3', [], renegotiate_tests()}, + {'tlsv1.2', [], renegotiate_tests()}, + {'tlsv1.1', [], renegotiate_tests()}, + {'tlsv1', [], renegotiate_tests()}, + {'sslv3', [], ssl3_renegotiate_tests()} + ]. + +renegotiate_tests() -> + [client_renegotiate, + server_renegotiate, + client_secure_renegotiate, + client_secure_renegotiate_fallback, + client_renegotiate_reused_session, + server_renegotiate_reused_session, + client_no_wrap_sequence_number, + server_no_wrap_sequence_number, + renegotiate_dos_mitigate_active, + renegotiate_dos_mitigate_passive, + renegotiate_dos_mitigate_absolute]. + +ssl3_renegotiate_tests() -> + [client_renegotiate, + server_renegotiate, + client_renegotiate_reused_session, + server_renegotiate_reused_session, + client_no_wrap_sequence_number, + server_no_wrap_sequence_number, + renegotiate_dos_mitigate_active, + renegotiate_dos_mitigate_passive, + renegotiate_dos_mitigate_absolute]. + +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + ssl_test_lib:make_rsa_cert(Config) + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). + +init_per_group(GroupName, Config) -> + ssl_test_lib:clean_tls_version(Config), + case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName, Config); + _ -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl:start(), + Config; + false -> + {skip, "Missing crypto support"} + end + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +client_renegotiate() -> + [{doc,"Test ssl:renegotiate/1 on client."}]. +client_renegotiate(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From erlang to erlang", + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + renegotiate, [Data]}}, + {options, [{reuse_sessions, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Client, ok, Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +client_secure_renegotiate() -> + [{doc,"Test ssl:renegotiate/1 on client."}]. +client_secure_renegotiate(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From erlang to erlang", + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options, [{secure_renegotiate, true} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + renegotiate, [Data]}}, + {options, [{reuse_sessions, false}, + {secure_renegotiate, true}| ClientOpts]}]), + + ssl_test_lib:check_result(Client, ok, Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +client_secure_renegotiate_fallback() -> + [{doc,"Test that we can set secure_renegotiate to false that is " + "fallback option, we however do not have a insecure server to test against!"}]. +client_secure_renegotiate_fallback(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From erlang to erlang", + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options, [{secure_renegotiate, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + renegotiate, [Data]}}, + {options, [{reuse_sessions, false}, + {secure_renegotiate, false}| ClientOpts]}]), + + ssl_test_lib:check_result(Client, ok, Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +server_renegotiate() -> + [{doc,"Test ssl:renegotiate/1 on server."}]. +server_renegotiate(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From erlang to erlang", + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + renegotiate, [Data]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options, [{reuse_sessions, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +client_renegotiate_reused_session() -> + [{doc,"Test ssl:renegotiate/1 on client when the ssl session will be reused."}]. +client_renegotiate_reused_session(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From erlang to erlang", + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + renegotiate_reuse_session, [Data]}}, + {options, [{reuse_sessions, true} | ClientOpts]}]), + + ssl_test_lib:check_result(Client, ok, Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +server_renegotiate_reused_session() -> + [{doc,"Test ssl:renegotiate/1 on server when the ssl session will be reused."}]. +server_renegotiate_reused_session(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From erlang to erlang", + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + renegotiate_reuse_session, [Data]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, + {options, [{reuse_sessions, true} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +client_no_wrap_sequence_number() -> + [{doc,"Test that erlang client will renegotiate session when", + "max sequence number celing is about to be reached. Although" + "in the testcase we use the test option renegotiate_at" + " to lower treashold substantially."}]. + +client_no_wrap_sequence_number(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + ErlData = "From erlang to erlang", + N = 12, + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Version = ssl_test_lib:protocol_version(Config, tuple), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, + trigger_renegotiate, [[ErlData, treashold(N, Version)]]}}, + {options, [{reuse_sessions, false}, + {renegotiate_at, N} | ClientOpts]}]), + + ssl_test_lib:check_result(Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +server_no_wrap_sequence_number() -> + [{doc, "Test that erlang server will renegotiate session when", + "max sequence number celing is about to be reached. Although" + "in the testcase we use the test option renegotiate_at" + " to lower treashold substantially."}]. + +server_no_wrap_sequence_number(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From erlang to erlang", + N = 12, + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, + trigger_renegotiate, [[Data, N+2]]}}, + {options, [{renegotiate_at, N} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{reuse_sessions, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +renegotiate_dos_mitigate_active() -> + [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row", + "immediately after each other"}]. +renegotiate_dos_mitigate_active(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + renegotiate_immediately, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Client, ok, Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +renegotiate_dos_mitigate_passive() -> + [{doc, "Mitigate DOS computational attack by not allowing client to renegotiate many times in a row", + "immediately after each other"}]. +renegotiate_dos_mitigate_passive(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + renegotiate_immediately, []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Client, ok, Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +renegotiate_dos_mitigate_absolute() -> + [{doc, "Mitigate DOS computational attack by not allowing client to initiate renegotiation"}]. +renegotiate_dos_mitigate_absolute(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, [{client_renegotiation, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + renegotiate_rejected, + []}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Client, ok, Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- +renegotiate(Socket, Data) -> + ct:log("Renegotiating ~n", []), + Result = ssl:renegotiate(Socket), + ct:log("Result ~p~n", [Result]), + ssl:send(Socket, Data), + case Result of + ok -> + ok; + Other -> + Other + end. + +renegotiate_reuse_session(Socket, Data) -> + %% Make sure session is registered + ct:sleep(?SLEEP), + renegotiate(Socket, Data). + +renegotiate_immediately(Socket) -> + _ = ssl_test_lib:active_recv(Socket, 11), + ok = ssl:renegotiate(Socket), + {error, renegotiation_rejected} = ssl:renegotiate(Socket), + ct:sleep(?RENEGOTIATION_DISABLE_TIME + ?SLEEP), + ok = ssl:renegotiate(Socket), + ct:log("Renegotiated again"), + ssl:send(Socket, "Hello world"), + ok. + +renegotiate_rejected(Socket) -> + _ = ssl_test_lib:active_recv(Socket, 11), + {error, renegotiation_rejected} = ssl:renegotiate(Socket), + {error, renegotiation_rejected} = ssl:renegotiate(Socket), + ct:sleep(?RENEGOTIATION_DISABLE_TIME +1), + {error, renegotiation_rejected} = ssl:renegotiate(Socket), + ct:log("Failed to renegotiate again"), + ssl:send(Socket, "Hello world"), + ok. + +%% First two clauses handles 1/n-1 splitting countermeasure Rizzo/Duong-Beast +treashold(N, {3,0}) -> + (N div 2) + 1; +treashold(N, {3,1}) -> + (N div 2) + 1; +treashold(N, _) -> + N + 1. + +erlang_ssl_receive(Socket, Data) -> + case ssl_test_lib:active_recv(Socket, length(Data)) of + Data -> + ok; + Other -> + ct:fail({{expected, Data}, {got, Other}}) + end. diff --git a/lib/ssl/test/ssl_session_SUITE.erl b/lib/ssl/test/ssl_session_SUITE.erl new file mode 100644 index 0000000000..aa79698a72 --- /dev/null +++ b/lib/ssl/test/ssl_session_SUITE.erl @@ -0,0 +1,377 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2007-2019. 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(ssl_session_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include("tls_handshake.hrl"). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +-define(SLEEP, 500). +-define(EXPIRE, 10). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +all() -> + [ + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'}, + {group, 'dtlsv1.2'}, + {group, 'dtlsv1'} + ]. + +groups() -> + [{'dtlsv1.2', [], session_tests()}, + {'dtlsv1', [], session_tests()}, + {'tlsv1.3', [], session_tests()}, + {'tlsv1.2', [], session_tests()}, + {'tlsv1.1', [], session_tests()}, + {'tlsv1', [], session_tests()}, + {'sslv3', [], session_tests()} + ]. + +session_tests() -> + [reuse_session, + reuse_session_expired, + server_does_not_want_to_reuse_session, + no_reuses_session_server_restart_new_cert, + no_reuses_session_server_restart_new_cert_file]. + + +init_per_suite(Config0) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + Config = ssl_test_lib:make_rsa_cert(Config0), + ssl_test_lib:make_dsa_cert(Config) + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). + +init_per_group(GroupName, Config) -> + ssl_test_lib:clean_tls_version(Config), + case ssl_test_lib:is_tls_version(GroupName) andalso ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName, Config); + _ -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl:start(), + Config; + false -> + {skip, "Missing crypto support"} + end + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +init_per_testcase(reuse_session_expired, Config) -> + ssl:stop(), + application:load(ssl), + ssl_test_lib:clean_env(), + application:set_env(ssl, session_lifetime, ?EXPIRE), + application:set_env(ssl, session_delay_cleanup_time, 500), + ssl:start(), + ct:timetrap({seconds, 30}), + Config; +init_per_testcase(_, Config) -> + ct:timetrap({seconds, 15}), + Config. + +end_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +reuse_session() -> + [{doc,"Test reuse of sessions (short handshake)"}]. +reuse_session(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + + ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config). +%%-------------------------------------------------------------------- +reuse_session_expired() -> + [{doc,"Test sessions is not reused when it has expired"}]. +reuse_session_expired(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server0 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {tcp_options, [{active, false}]}, + {options, ServerOpts}]), + Port0 = ssl_test_lib:inet_port(Server0), + + Client0 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), + Server0 ! listen, + + Client1 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, ClientOpts}]), + + SID = receive + {Client0, Id0} -> + Id0 + end, + + receive + {Client1, SID} -> + ok + after ?SLEEP -> + ct:fail(session_not_reused) + end, + + Server0 ! listen, + + %% Make sure session is unregistered due to expiration + ct:sleep((?EXPIRE*2)), + + make_sure_expired(Hostname, Port0, SID), + + Client2 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, ClientOpts}]), + receive + {Client2, SID} -> + ct:fail(session_reused_when_session_expired); + {Client2, _} -> + ok + end, + process_flag(trap_exit, false), + ssl_test_lib:close(Server0), + ssl_test_lib:close(Client0), + ssl_test_lib:close(Client1), + ssl_test_lib:close(Client2). + +make_sure_expired(Host, Port, Id) -> + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + ClientCache = element(2, State), + + case ssl_session_cache:lookup(ClientCache, {{Host, Port}, Id}) of + undefined -> + ok; + #session{is_resumable = false} -> + ok; + _ -> + ct:sleep(?SLEEP), + make_sure_expired(Host, Port, Id) + end. + +%%-------------------------------------------------------------------- +server_does_not_want_to_reuse_session() -> + [{doc,"Test reuse of sessions (short handshake)"}]. +server_does_not_want_to_reuse_session(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, session_info_result, []}}, + {options, [{reuse_session, fun(_,_,_,_) -> + false + end} | + ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, no_result, []}}, + {from, self()}, {options, ClientOpts}]), + SessionInfo = + receive + {Server, Info} -> + Info + end, + + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, + + %% Make sure session is registered + ct:sleep(?SLEEP), + + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + receive + {Client1, SessionInfo} -> + ct:fail(session_reused_when_server_does_not_want_to); + {Client1, _Other} -> + ok + end, + ssl_test_lib:close(Client0), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client1). + +no_reuses_session_server_restart_new_cert() -> + [{doc,"Check that a session is not reused if the server is restarted with a new cert."}]. +no_reuses_session_server_restart_new_cert(Config) when is_list(Config) -> + + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + DsaServerOpts = ssl_test_lib:ssl_options(server_dsa_verify_opts, Config), + DsaClientOpts = ssl_test_lib:ssl_options(client_dsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, session_info_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, no_result, []}}, + {from, self()}, {options, ClientOpts}]), + SessionInfo = + receive + {Server, Info} -> + Info + end, + + %% Make sure session is registered + ct:sleep(?SLEEP), + Monitor = erlang:monitor(process, Server), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client0), + receive + {'DOWN', Monitor, _, _, _} -> + ok + end, + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{reuseaddr, true} | DsaServerOpts]}]), + + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_info_result, []}}, + {from, self()}, {options, DsaClientOpts}]), + receive + {Client1, SessionInfo} -> + ct:fail(session_reused_when_server_has_new_cert); + {Client1, _Other} -> + ok + end, + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- +no_reuses_session_server_restart_new_cert_file() -> + [{doc,"Check that a session is not reused if a server is restarted with a new " + "cert contained in a file with the same name as the old cert."}]. + +no_reuses_session_server_restart_new_cert_file(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + DsaServerOpts = ssl_test_lib:ssl_options(server_dsa_verify_opts, Config), + PrivDir = proplists:get_value(priv_dir, Config), + + NewServerOpts0 = ssl_test_lib:new_config(PrivDir, ServerOpts), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, session_info_result, []}}, + {options, NewServerOpts0}]), + Port = ssl_test_lib:inet_port(Server), + Client0 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, no_result, []}}, + {from, self()}, {options, ClientOpts}]), + SessionInfo = + receive + {Server, Info} -> + Info + end, + + %% Make sure session is registered and we get + %% new file time stamp when calling new_config! + ct:sleep(?SLEEP* 2), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client0), + + ssl:clear_pem_cache(), + + NewServerOpts1 = ssl_test_lib:new_config(PrivDir, DsaServerOpts), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{reuseaddr, true} | NewServerOpts1]}]), + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_info_result, []}}, + {from, self()}, {options, ClientOpts}]), + receive + {Client1, SessionInfo} -> + ct:fail(session_reused_when_server_has_new_cert); + {Client1, _Other} -> + ok + end, + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- diff --git a/lib/ssl/test/ssl_sni_SUITE.erl b/lib/ssl/test/ssl_sni_SUITE.erl index 7629d75100..e68ea2c99d 100644 --- a/lib/ssl/test/ssl_sni_SUITE.erl +++ b/lib/ssl/test/ssl_sni_SUITE.erl @@ -36,7 +36,6 @@ all() -> [{group, 'tlsv1.2'}, {group, 'tlsv1.1'}, {group, 'tlsv1'}, - {group, 'sslv3'}, {group, 'dtlsv1.2'}, {group, 'dtlsv1'} ]. @@ -46,7 +45,6 @@ groups() -> {'tlsv1.2', [], sni_tests()}, {'tlsv1.1', [], sni_tests()}, {'tlsv1', [], sni_tests()}, - {'sslv3', [], sni_tests()}, {'dtlsv1.2', [], sni_tests()}, {'dtlsv1', [], sni_tests()} ]. @@ -61,7 +59,8 @@ sni_tests() -> dns_name, ip_fallback, no_ip_fallback, - dns_name_reuse]. + dns_name_reuse, + customize_hostname_check]. init_per_suite(Config0) -> catch crypto:stop(), @@ -88,12 +87,10 @@ end_per_suite(_) -> ssl:stop(), application:stop(crypto). -init_per_testcase(TestCase, Config) when TestCase == ip_fallback; - TestCase == no_ip_fallback; - TestCase == dns_name_reuse -> +init_per_testcase(customize_hostname_check, Config) -> ssl_test_lib:ct_log_supported_protocol_versions(Config), - ct:log("Ciphers: ~p~n ", [ ssl:cipher_suites()]), - ct:timetrap({seconds, 20}), + ssl_test_lib:clean_start(), + ct:timetrap({seconds, 5}), Config; init_per_testcase(_TestCase, Config) -> ssl_test_lib:ct_log_supported_protocol_versions(Config), @@ -236,7 +233,60 @@ dns_name_reuse(Config) -> {mfa, {ssl_test_lib, session_info_result, []}}, {from, self()}, {options, [{verify, verify_peer} | ClientConf]}]), - ssl_test_lib:check_client_alert(Client1, handshake_failure). + ssl_test_lib:check_client_alert(Client1, handshake_failure), + ssl_test_lib:close(Client0). + + +customize_hostname_check() -> + [{doc,"Test option customize_hostname_check."}]. +customize_hostname_check(Config) when is_list(Config) -> + Ext = [#'Extension'{extnID = ?'id-ce-subjectAltName', + extnValue = [{dNSName, "*.example.org"}], + critical = false} + ], + #{server_config := ServerOpts0, + client_config := ClientOpts0} = ssl_test_lib:make_cert_chains_pem(rsa, [{server_chain, + [[], + [], + [{extensions, Ext}] + ]}], + Config, "https_hostname_convention"), + ClientOpts = ssl_test_lib:ssl_options(ClientOpts0, Config), + ServerOpts = ssl_test_lib:ssl_options(ServerOpts0, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + CustomFun = public_key:pkix_verify_hostname_match_fun(https), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, + [{verify, verify_peer}, + {server_name_indication, "other.example.org"}, + {customize_hostname_check, + [{match_fun, CustomFun}]} | ClientOpts] + }]), + ssl_test_lib:check_result(Server, ok, Client, ok), + + Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, + + Client1 = ssl_test_lib:start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{verify, verify_peer}, + {server_name_indication, "other.example.org"} | ClientOpts]} + ]), + ssl_test_lib:check_client_alert(Server, Client1, handshake_failure). %%-------------------------------------------------------------------- %% Internal Functions ------------------------------------------------ diff --git a/lib/ssl/test/ssl_socket_SUITE.erl b/lib/ssl/test/ssl_socket_SUITE.erl new file mode 100644 index 0000000000..d648f2f9e1 --- /dev/null +++ b/lib/ssl/test/ssl_socket_SUITE.erl @@ -0,0 +1,437 @@ +%% +%% Copyright Ericsson AB 2019-2019. 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(ssl_socket_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +-define(SLEEP, 500). +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +all() -> + [ + {group, tls}, + {group, dtls} + ]. + +groups() -> + [ + {tls,[], socket_tests() ++ raw_inet_opt()}, + {dtls,[], socket_tests()} + ]. + +socket_tests() -> + [ + getstat, + socket_options, + invalid_inet_get_option, + invalid_inet_get_option_not_list, + invalid_inet_get_option_improper_list, + invalid_inet_set_option, + invalid_inet_set_option_not_list, + invalid_inet_set_option_improper_list + ]. + +raw_inet_opt() -> + [ + raw_inet_option + ]. + + +init_per_suite(Config0) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + ssl_test_lib:make_rsa_cert(Config0) + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:unload(ssl), + application:stop(crypto). + +init_per_group(dtls, Config) -> + [{protocol_opts, [{protocol, dtls}]} | proplists:delete(protocol_opts, Config)]; +init_per_group(tls, Config) -> + [{protocol_opts, [{protocol, tls}]} | proplists:delete(protocol_opts, Config)]; +init_per_group(_GroupName, Config) -> + [{client_type, erlang}, + {server_type, erlang} | Config]. + +end_per_group(_GroupName, Config) -> + Config. + +init_per_testcase(raw_inet_option, Config) -> + ct:timetrap({seconds, 5}), + case os:type() of + {unix,linux} -> + Config; + _ -> + {skip, "Raw options are platform-specific"} + end; +init_per_testcase(_TestCase, Config) -> + ct:timetrap({seconds, 5}), + Config. + +end_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +getstat() -> + [{doc,"Test API function getstat/2"}]. + +getstat(Config) when is_list(Config) -> + ClientOpts = ?config(client_rsa_opts, Config), + ServerOpts = ?config(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false} | ServerOpts]}]), + Port1 = ssl_test_lib:inet_port(Server1), + Server2 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false} | ServerOpts]}]), + Port2 = ssl_test_lib:inet_port(Server2), + {ok, ActiveC} = rpc:call(ClientNode, ssl, connect, + [Hostname,Port1,[{active, once}|ClientOpts]]), + {ok, PassiveC} = rpc:call(ClientNode, ssl, connect, + [Hostname,Port2,[{active, false}|ClientOpts]]), + + ct:log("Testcase ~p, Client ~p Servers ~p, ~p ~n", + [self(), self(), Server1, Server2]), + + %% We only check that the values are non-zero initially + %% (due to the handshake), and that sending more changes the values. + + %% Passive socket. + + {ok, InitialStats} = ssl:getstat(PassiveC), + ct:pal("InitialStats ~p~n", [InitialStats]), + [true] = lists:usort([0 =/= proplists:get_value(Name, InitialStats) + || Name <- [recv_cnt, recv_oct, recv_avg, recv_max, send_cnt, send_oct, send_avg, send_max]]), + + ok = ssl:send(PassiveC, "Hello world"), + wait_for_send(PassiveC), + {ok, SStats} = ssl:getstat(PassiveC, [send_cnt, send_oct]), + ct:pal("SStats ~p~n", [SStats]), + [true] = lists:usort([proplists:get_value(Name, SStats) =/= proplists:get_value(Name, InitialStats) + || Name <- [send_cnt, send_oct]]), + + %% Active socket. + + {ok, InitialAStats} = ssl:getstat(ActiveC), + ct:pal("InitialAStats ~p~n", [InitialAStats]), + [true] = lists:usort([0 =/= proplists:get_value(Name, InitialAStats) + || Name <- [recv_cnt, recv_oct, recv_avg, recv_max, send_cnt, send_oct, send_avg, send_max]]), + + _ = receive + {ssl, ActiveC, _} -> + ok + after + ?SLEEP -> + exit(timeout) + end, + + ok = ssl:send(ActiveC, "Hello world"), + wait_for_send(ActiveC), + {ok, ASStats} = ssl:getstat(ActiveC, [send_cnt, send_oct]), + ct:pal("ASStats ~p~n", [ASStats]), + [true] = lists:usort([proplists:get_value(Name, ASStats) =/= proplists:get_value(Name, InitialAStats) + || Name <- [send_cnt, send_oct]]), + + ok. +%%-------------------------------------------------------------------- +socket_options() -> + [{doc,"Test API function getopts/2 and setopts/2"}]. + +socket_options(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Values = [{mode, list}, {active, true}], + %% Shall be the reverse order of Values! + Options = [active, mode], + + NewValues = [{mode, binary}, {active, once}], + %% Shall be the reverse order of NewValues! + NewOptions = [active, mode], + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, socket_options_result, + [Options, Values, NewOptions, NewValues]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, socket_options_result, + [Options, Values, NewOptions, NewValues]}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + + {ok, Listen} = ssl:listen(0, ServerOpts), + {ok,[{mode,list}]} = ssl:getopts(Listen, [mode]), + ok = ssl:setopts(Listen, [{mode, binary}]), + {ok,[{mode, binary}]} = ssl:getopts(Listen, [mode]), + {ok,[{recbuf, _}]} = ssl:getopts(Listen, [recbuf]), + ssl:close(Listen). + +%%-------------------------------------------------------------------- +raw_inet_option() -> + [{doc,"Ensure that a single 'raw' option is passed to ssl:listen correctly."}]. + +raw_inet_option(Config) when is_list(Config) -> + % 'raw' option values are platform-specific; these are the Linux values: + IpProtoTcp = 6, + % Use TCP_KEEPIDLE, because (e.g.) TCP_MAXSEG can't be read back reliably. + TcpKeepIdle = 4, + KeepAliveTimeSecs = 55, + LOptions = [{raw, IpProtoTcp, TcpKeepIdle, <<KeepAliveTimeSecs:32/native>>}], + {ok, LSocket} = ssl:listen(0, LOptions), + % Per http://www.erlang.org/doc/man/inet.html#getopts-2, we have to specify + % exactly which raw option we want, and the size of the buffer. + {ok, [{raw, IpProtoTcp, TcpKeepIdle, <<KeepAliveTimeSecs:32/native>>}]} = + ssl:getopts(LSocket, [{raw, IpProtoTcp, TcpKeepIdle, 4}]). + +%%-------------------------------------------------------------------- + +invalid_inet_get_option() -> + [{doc,"Test handling of invalid inet options in getopts"}]. + +invalid_inet_get_option(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, get_invalid_inet_option, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +invalid_inet_get_option_not_list() -> + [{doc,"Test handling of invalid type in getopts"}]. + +invalid_inet_get_option_not_list(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, get_invalid_inet_option_not_list, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +invalid_inet_get_option_improper_list() -> + [{doc,"Test handling of invalid type in getopts"}]. + +invalid_inet_get_option_improper_list(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, get_invalid_inet_option_improper_list, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +invalid_inet_set_option() -> + [{doc,"Test handling of invalid inet options in setopts"}]. + +invalid_inet_set_option(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, set_invalid_inet_option, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +invalid_inet_set_option_not_list() -> + [{doc,"Test handling of invalid type in setopts"}]. + +invalid_inet_set_option_not_list(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, set_invalid_inet_option_not_list, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +invalid_inet_set_option_improper_list() -> + [{doc,"Test handling of invalid tye in setopts"}]. + +invalid_inet_set_option_improper_list(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, set_invalid_inet_option_improper_list, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- +socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) -> + %% Test get/set emulated opts + {ok, DefaultValues} = ssl:getopts(Socket, Options), + ssl:setopts(Socket, NewValues), + {ok, NewValues} = ssl:getopts(Socket, NewOptions), + %% Test get/set inet opts + {ok,[{reuseaddr, _}]} = ssl:getopts(Socket, [reuseaddr]), + {ok, All} = ssl:getopts(Socket, []), + ct:log("All opts ~p~n", [All]), + ok. + +get_invalid_inet_option(Socket) -> + {error, {options, {socket_options, foo, _}}} = ssl:getopts(Socket, [foo]), + ok. + +get_invalid_inet_option_not_list(Socket) -> + {error, {options, {socket_options, some_invalid_atom_here}}} + = ssl:getopts(Socket, some_invalid_atom_here), + ok. + +get_invalid_inet_option_improper_list(Socket) -> + {error, {options, {socket_options, foo,_}}} = ssl:getopts(Socket, [packet | foo]), + ok. + +set_invalid_inet_option(Socket) -> + {error, {options, {socket_options, {packet, foo}}}} = ssl:setopts(Socket, [{packet, foo}]), + {error, {options, {socket_options, {header, foo}}}} = ssl:setopts(Socket, [{header, foo}]), + {error, {options, {socket_options, {active, foo}}}} = ssl:setopts(Socket, [{active, foo}]), + {error, {options, {socket_options, {mode, foo}}}} = ssl:setopts(Socket, [{mode, foo}]), + ok. + +set_invalid_inet_option_not_list(Socket) -> + {error, {options, {not_a_proplist, some_invalid_atom_here}}} + = ssl:setopts(Socket, some_invalid_atom_here), + ok. + +set_invalid_inet_option_improper_list(Socket) -> + {error, {options, {not_a_proplist, [{packet, 0} | {foo, 2}]}}} = + ssl:setopts(Socket, [{packet, 0} | {foo, 2}]), + ok. + +wait_for_send(Socket) -> + %% Make sure TLS process processed send message event + _ = ssl:connection_information(Socket). + diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index d46ba1f787..7dd27fb5cb 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -45,9 +45,18 @@ run_where(_, ipv6) -> Host = rpc:call(ServerNode, net_adm, localhost, []), {ClientNode, ServerNode, Host}. -node_to_hostip(Node) -> +node_to_hostip(Node, Role) -> [_ , Host] = string:tokens(atom_to_list(Node), "@"), {ok, Address} = inet:getaddr(Host, inet), + %% Convert client addresses in 127.0.0.0/24 subnet to the atom 'localhost'. + %% This is a workaround for testcase problems caused by the fact that + %% inet:peername/1 and inet:getaddr/2 return different addresses when + %% running on localhost. + normalize_loopback(Address, Role). + +normalize_loopback({127,_,_,_}, client) -> + localhost; +normalize_loopback(Address, _) -> Address. start_server(Args) -> @@ -209,6 +218,55 @@ start_server_transport_control(Args) -> Result end. +start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, OpensslServerOpts, Data, Callback) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ClientOpts = ErlangClientOpts ++ ClientOpts0, + + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + + Data = "From openssl to erlang", + + Port = ssl_test_lib:inet_port(node()), + CaCertFile = proplists:get_value(cacertfile, ServerOpts), + CertFile = proplists:get_value(certfile, ServerOpts), + KeyFile = proplists:get_value(keyfile, ServerOpts), + Version = ssl_test_lib:protocol_version(Config), + + Exe = "openssl", + Args = case OpensslServerOpts of + [] -> + ["s_server", "-accept", + integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, + "-cert", CertFile,"-key", KeyFile]; + [Opt, Value] -> + ["s_server", Opt, Value, "-accept", + integer_to_list(Port), ssl_test_lib:version_flag(Version), + "-CAfile", CaCertFile, + "-cert", CertFile,"-key", KeyFile] + end, + + OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), + + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, + active_recv, [length(Data)]}}, + {options, ClientOpts}]), + + Callback(Client, OpensslPort), + + %% Clean close down! Server needs to be closed first !! + ssl_test_lib:close_port(OpensslPort), + + ssl_test_lib:close(Client), + process_flag(trap_exit, false). + transport_accept_abuse(Opts) -> Port = proplists:get_value(port, Opts), @@ -224,6 +282,34 @@ transport_accept_abuse(Opts) -> _ = ssl:handshake(AcceptSocket, infinity), Pid ! {self(), ok}. +start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenSSLClientOpts, Data, Callback) -> + process_flag(trap_exit, true), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), + ServerOpts = ErlangServerOpts ++ ServerOpts0, + + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, active_recv, [length(Data)]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Version = ssl_test_lib:protocol_version(Config), + + Exe = "openssl", + Args = ["s_client"] ++ OpenSSLClientOpts ++ ["-msg", "-connect", + hostname_format(Hostname) ++ ":" ++ integer_to_list(Port), + ssl_test_lib:version_flag(Version)], + + OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), + + Callback(Server, OpenSslPort), + + ssl_test_lib:close(Server), + + ssl_test_lib:close_port(OpenSslPort), + process_flag(trap_exit, false). transport_switch_control(Opts) -> Port = proplists:get_value(port, Opts), @@ -393,14 +479,16 @@ close(Pid, Timeout) -> exit(Pid, kill) end. -check_result(Server, ServerMsg, Client, ClientMsg) -> +check_result(Server, ServerMsg, Client, ClientMsg) -> + {ClientIP, ClientPort} = get_ip_port(ServerMsg), receive {Server, ServerMsg} -> check_result(Client, ClientMsg); - + %% Workaround to accept local addresses (127.0.0.0/24) + {Server, {ok, {{127,_,_,_}, ClientPort}}} when ClientIP =:= localhost -> + check_result(Client, ClientMsg); {Client, ClientMsg} -> check_result(Server, ServerMsg); - {Port, {data,Debug}} when is_port(Port) -> ct:log("~p:~p~n Openssl ~s~n",[?MODULE,?LINE, Debug]), check_result(Server, ServerMsg, Client, ClientMsg); @@ -413,10 +501,14 @@ check_result(Server, ServerMsg, Client, ClientMsg) -> ct:fail(Reason) end. -check_result(Pid, Msg) -> +check_result(Pid, Msg) -> + {ClientIP, ClientPort} = get_ip_port(Msg), receive {Pid, Msg} -> ok; + %% Workaround to accept local addresses (127.0.0.0/24) + {Pid, {ok, {{127,_,_,_}, ClientPort}}} when ClientIP =:= localhost -> + ok; {Port, {data,Debug}} when is_port(Port) -> ct:log("~p:~p~n Openssl ~s~n",[?MODULE,?LINE, Debug]), check_result(Pid,Msg); @@ -428,41 +520,49 @@ check_result(Pid, Msg) -> {got, Unexpected}}, ct:fail(Reason) end. + + +get_ip_port({ok,{ClientIP, ClientPort}}) -> + {ClientIP, ClientPort}; +get_ip_port(_) -> + {undefined, undefined}. + + check_server_alert(Pid, Alert) -> receive {Pid, {error, {tls_alert, {Alert, STxt}}}} -> check_server_txt(STxt), + ok; + {Pid, {error, closed}} -> ok end. check_server_alert(Server, Client, Alert) -> receive {Server, {error, {tls_alert, {Alert, STxt}}}} -> check_server_txt(STxt), - receive - {Client, {error, {tls_alert, {Alert, CTxt}}}} -> - check_client_txt(CTxt), - ok; - {Client, {error, closed}} -> - ok - end + check_client_alert(Client, Alert) end. check_client_alert(Pid, Alert) -> receive {Pid, {error, {tls_alert, {Alert, CTxt}}}} -> check_client_txt(CTxt), + ok; + {Pid, {ssl_error, _, {tls_alert, {Alert, CTxt}}}} -> + check_client_txt(CTxt), + ok; + {Pid, {error, closed}} -> ok end. check_client_alert(Server, Client, Alert) -> receive {Client, {error, {tls_alert, {Alert, CTxt}}}} -> check_client_txt(CTxt), - receive - {Server, {error, {tls_alert, {Alert, STxt}}}} -> - check_server_txt(STxt), - ok; - {Server, {error, closed}} -> - ok - end + check_server_alert(Server, Alert); + {Client, {ssl_error, _, {tls_alert, {Alert, CTxt}}}} -> + check_client_txt(CTxt), + ok; + {Client, {error, closed}} -> + ok end. check_server_txt("TLS server" ++ _) -> ok; @@ -548,8 +648,7 @@ cert_options(Config) -> "badcert.pem"]), BadKeyFile = filename:join([proplists:get_value(priv_dir, Config), "badkey.pem"]), - PskSharedSecret = <<1,2,3,4,5,6,7,8,9,10,11,12,13,14,15>>, - + [{client_opts, [{cacertfile, ClientCaCertFile}, {certfile, ClientCertFile}, {keyfile, ClientKeyFile}]}, @@ -563,30 +662,6 @@ cert_options(Config) -> {ssl_imp, new}]}, {server_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ServerCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, - {client_psk, [{ssl_imp, new}, - {psk_identity, "Test-User"}, - {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]}, - {server_psk, [{ssl_imp, new},{reuseaddr, true}, - {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, - {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]}, - {server_psk_hint, [{ssl_imp, new},{reuseaddr, true}, - {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, - {psk_identity, "HINT"}, - {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]}, - {server_psk_anon, [{ssl_imp, new},{reuseaddr, true}, - {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]}, - {server_psk_anon_hint, [{ssl_imp, new},{reuseaddr, true}, - {psk_identity, "HINT"}, - {user_lookup_fun, {fun user_lookup/3, PskSharedSecret}}]}, - {client_srp, [{ssl_imp, new}, - {srp_identity, {"Test-User", "secret"}}]}, - {server_srp, [{ssl_imp, new},{reuseaddr, true}, - {certfile, ServerCertFile}, {keyfile, ServerKeyFile}, - {user_lookup_fun, {fun user_lookup/3, undefined}}, - {ciphers, srp_suites()}]}, - {server_srp_anon, [{ssl_imp, new},{reuseaddr, true}, - {user_lookup_fun, {fun user_lookup/3, undefined}}, - {ciphers, srp_anon_suites()}]}, {server_verification_opts, [{ssl_imp, new},{reuseaddr, true}, {cacertfile, ClientCaCertFile}, {certfile, ServerCertFile}, {keyfile, ServerKeyFile}]}, @@ -627,15 +702,32 @@ make_dsa_cert(Config) -> [{server_dsa_opts, ServerConf}, {server_dsa_verify_opts, [{verify, verify_peer} | ServerConf]}, - {client_dsa_opts, ClientConf}, - {server_srp_dsa, [{user_lookup_fun, {fun user_lookup/3, undefined}}, - {ciphers, srp_dss_suites()} | ServerConf]}, - {client_srp_dsa, [{srp_identity, {"Test-User", "secret"}} - | ClientConf]} + {client_dsa_opts, ClientConf} | Config]; false -> Config end. + + +make_cert_chains_der(Alg, UserConf) -> + ClientChain = proplists:get_value(client_chain, UserConf, default_cert_chain_conf()), + ServerChain = proplists:get_value(server_chain, UserConf, default_cert_chain_conf()), + CertChainConf = gen_conf(Alg, Alg, ClientChain, ServerChain), + public_key:pkix_test_data(CertChainConf). + +make_cert_chains_pem(Alg, UserConf, Config, Suffix) -> + ClientChain = proplists:get_value(client_chain, UserConf, default_cert_chain_conf()), + ServerChain = proplists:get_value(server_chain, UserConf, default_cert_chain_conf()), + CertChainConf = gen_conf(Alg, Alg, ClientChain, ServerChain), + ClientFileBase = filename:join([proplists:get_value(priv_dir, Config), atom_to_list(Alg) ++ Suffix]), + ServerFileBase = filename:join([proplists:get_value(priv_dir, Config), atom_to_list(Alg) ++ Suffix]), + GenCertData = public_key:pkix_test_data(CertChainConf), + Conf = x509_test:gen_pem_config_files(GenCertData, ClientFileBase, ServerFileBase), + CConf = proplists:get_value(client_config, Conf), + SConf = proplists:get_value(server_config, Conf), + #{server_config => SConf, + client_config => CConf}. + make_rsa_cert_chains(UserConf, Config, Suffix) -> ClientChain = proplists:get_value(client_chain, UserConf, default_cert_chain_conf()), ServerChain = proplists:get_value(server_chain, UserConf, default_cert_chain_conf()), @@ -1133,6 +1225,52 @@ basic_test(COpts, SOpts, Config) -> gen_check_result(Server, SType, Client, CType), stop(Server, Client). +basic_alert(ClientOpts, ServerOpts, Config, Alert) -> + SType = proplists:get_value(server_type, Config), + CType = proplists:get_value(client_type, Config), + run_basic_alert(SType, CType, ClientOpts, ServerOpts, Config, Alert). + +run_basic_alert(erlang, erlang, ClientOpts, ServerOpts, Config, Alert) -> + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ServerOpts}]), + + Port = inet_port(Server), + + Client = start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + check_server_alert(Server, Client, Alert); +run_basic_alert(openssl = SType, erlang, ClientOpts, ServerOpts, Config, Alert) -> + {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), + {_Server, Port} = start_server(SType, ClientOpts, ServerOpts, Config), + ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), + Client = start_client_error([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ClientOpts}]), + + check_client_alert(Client, Alert); +run_basic_alert(erlang, openssl = CType, ClientOpts, ServerOpts, Config, Alert) -> + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = start_server_error([{node, ServerNode}, {port, 0}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ServerOpts}]), + Port = inet_port(Server), + start_client(CType, Port, ClientOpts, Config), + + check_server_alert(Server, Alert). + + ecc_test(Expect, COpts, SOpts, CECCOpts, SECCOpts, Config) -> {Server, Port} = start_server_ecc(erlang, SOpts, Expect, SECCOpts, Config), Client = start_client_ecc(erlang, Port, COpts, Expect, CECCOpts, Config), @@ -1174,15 +1312,22 @@ start_basic_client(openssl, Version, Port, ClientOpts) -> OpenSslPort. start_client(openssl, Port, ClientOpts, Config) -> - Cert = proplists:get_value(certfile, ClientOpts), - Key = proplists:get_value(keyfile, ClientOpts), - CA = proplists:get_value(cacertfile, ClientOpts), Version = ssl_test_lib:protocol_version(Config), Exe = "openssl", - Args0 = ["s_client", "-verify", "2", "-port", integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-cert", Cert, "-CAfile", CA, - "-key", Key, "-host","localhost", "-msg", "-debug"], + Ciphers = proplists:get_value(ciphers, ClientOpts, ssl:cipher_suites(default,Version)), + Groups0 = proplists:get_value(groups, ClientOpts), + CertArgs = openssl_cert_options(ClientOpts, client), + Exe = "openssl", + Args0 = case Groups0 of + undefined -> + ["s_client", "-verify", "2", "-port", integer_to_list(Port), cipher_flag(Version), + ciphers(Ciphers, Version), + ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"]; + Group -> + ["s_client", "-verify", "2", "-port", integer_to_list(Port), cipher_flag(Version), + ciphers(Ciphers, Version), "-groups", Group, + ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"] + end, Args = maybe_force_ipv4(Args0), OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), true = port_command(OpenSslPort, "Hello world"), @@ -1234,24 +1379,54 @@ start_server(openssl, ClientOpts, ServerOpts, Config) -> Port = inet_port(node()), Version = protocol_version(Config), Exe = "openssl", - CertArgs = openssl_cert_options(ServerOpts), - [Cipher|_] = proplists:get_value(ciphers, ClientOpts, ssl:cipher_suites(default,Version)), - Args = ["s_server", "-accept", integer_to_list(Port), "-cipher", - ssl_cipher_format:suite_map_to_openssl_str(Cipher), - ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"], + CertArgs = openssl_cert_options(ServerOpts, server), + Ciphers = proplists:get_value(ciphers, ClientOpts, ssl:cipher_suites(default,Version)), + Groups0 = proplists:get_value(groups, ServerOpts), + Args = case Groups0 of + undefined -> + ["s_server", "-accept", integer_to_list(Port), cipher_flag(Version), + ciphers(Ciphers, Version), + ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"]; + Group -> + ["s_server", "-accept", integer_to_list(Port), cipher_flag(Version), + ciphers(Ciphers, Version), "-groups", Group, + ssl_test_lib:version_flag(Version)] ++ CertArgs ++ ["-msg", "-debug"] + end, OpenSslPort = portable_open_port(Exe, Args), true = port_command(OpenSslPort, "Hello world"), {OpenSslPort, Port}; start_server(erlang, _, ServerOpts, Config) -> {_, ServerNode, _} = ssl_test_lib:run_where(Config), KeyEx = proplists:get_value(check_keyex, Config, false), + Versions = protocol_versions(Config), Server = start_server([{node, ServerNode}, {port, 0}, {from, self()}, {mfa, {ssl_test_lib, check_key_exchange_send_active, [KeyEx]}}, - {options, [{verify, verify_peer} | ServerOpts]}]), + {options, [{verify, verify_peer}, {versions, Versions} | ServerOpts]}]), {Server, inet_port(Server)}. + +cipher_flag('tlsv1.3') -> + "-ciphersuites"; +cipher_flag(_) -> + "-cipher". + +ciphers(Ciphers, Version) -> + Strs = [ssl_cipher_format:suite_map_to_openssl_str(Cipher) || Cipher <- Ciphers], + ciphers_concat(Version, Strs, ""). + +ciphers_concat(_, [], [":" | Acc]) -> + lists:append(lists:reverse(Acc)); +ciphers_concat('tlsv1.3' = Version, [Head| Tail], Acc) -> + case Head of + "TLS" ++ _ -> + ciphers_concat(Version, Tail, [":", Head | Acc]); + _ -> + ciphers_concat(Version, Tail, Acc) + end; +ciphers_concat(Version, [Head| Tail], Acc) -> + ciphers_concat(Version, Tail, [":", Head | Acc]). start_server_with_raw_key(erlang, ServerOpts, Config) -> {_, ServerNode, _} = ssl_test_lib:run_where(Config), @@ -1306,23 +1481,31 @@ stop(Client, Server) -> close(Client). -openssl_cert_options(ServerOpts) -> - Cert = proplists:get_value(certfile, ServerOpts, undefined), - Key = proplists:get_value(keyfile, ServerOpts, undefined), - CA = proplists:get_value(cacertfile, ServerOpts, undefined), +openssl_cert_options(Opts, Role) -> + Cert = proplists:get_value(certfile, Opts, undefined), + Key = proplists:get_value(keyfile, Opts, undefined), + CA = proplists:get_value(cacertfile, Opts, undefined), case CA of undefined -> case cert_option("-cert", Cert) ++ cert_option("-key", Key) of - [] -> + [] when Role == server -> ["-nocert"]; Other -> Other end; _ -> cert_option("-cert", Cert) ++ cert_option("-CAfile", CA) ++ - cert_option("-key", Key) ++ ["-verify", "2"] + cert_option("-key", Key) ++ openssl_verify(Opts) ++ ["2"] end. +openssl_verify(Opts) -> + case proplists:get_value(fail_if_no_peer_cert, Opts, undefined) of + true -> + ["-Verify"]; + _ -> + ["-verify"] + end. + cert_option(_, undefined) -> []; cert_option(Opt, Value) -> @@ -1650,6 +1833,8 @@ is_tls_version('dtlsv1.2') -> true; is_tls_version('dtlsv1') -> true; +is_tls_version('tlsv1.3') -> + true; is_tls_version('tlsv1.2') -> true; is_tls_version('tlsv1.1') -> @@ -1980,12 +2165,61 @@ check_sane_openssl_version(Version) -> false; {'tlsv1.1', "OpenSSL 0" ++ _} -> false; + {'tlsv1', "OpenSSL 0" ++ _} -> + false; {_, _} -> true end; false -> false end. +check_sane_openssl_renegotaite(Config, Version) when Version == 'tlsv1.1'; + Version == 'tlsv1.2' -> + case os:cmd("openssl version") of + "OpenSSL 1.0.1c" ++ _ -> + {skip, "Known renegotiation bug in OpenSSL"}; + "OpenSSL 1.0.1b" ++ _ -> + {skip, "Known renegotiation bug in OpenSSL"}; + "OpenSSL 1.0.1a" ++ _ -> + {skip, "Known renegotiation bug in OpenSSL"}; + "OpenSSL 1.0.1 " ++ _ -> + {skip, "Known renegotiation bug in OpenSSL"}; + _ -> + check_sane_openssl_renegotaite(Config) + end; +check_sane_openssl_renegotaite(Config, _) -> + check_sane_openssl_renegotaite(Config). + +check_sane_openssl_renegotaite(Config) -> + case os:cmd("openssl version") of + "OpenSSL 1.0.0" ++ _ -> + {skip, "Known renegotiation bug in OpenSSL"}; + "OpenSSL 0.9.8" ++ _ -> + {skip, "Known renegotiation bug in OpenSSL"}; + "OpenSSL 0.9.7" ++ _ -> + {skip, "Known renegotiation bug in OpenSSL"}; + _ -> + Config + end. + +workaround_openssl_s_clinent() -> + %% http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=683159 + %% https://bugs.archlinux.org/task/33919 + %% Bug seems to manifests it self if TLS version is not + %% explicitly specified + case os:cmd("openssl version") of + "OpenSSL 1.0.1c" ++ _ -> + ["-no_tls1_2"]; + "OpenSSL 1.0.1d" ++ _ -> + ["-no_tls1_2"]; + "OpenSSL 1.0.1e" ++ _ -> + ["-no_tls1_2"]; + "OpenSSL 1.0.1f" ++ _ -> + ["-no_tls1_2"]; + _ -> + [] + end. + enough_openssl_crl_support("OpenSSL 0." ++ _) -> false; enough_openssl_crl_support(_) -> true. @@ -2035,8 +2269,8 @@ filter_suites(Ciphers0, AtomVersion) -> ++ ssl_cipher:anonymous_suites(Version) ++ ssl_cipher:psk_suites(Version) ++ ssl_cipher:psk_suites_anon(Version) - ++ ssl_cipher:srp_suites() - ++ ssl_cipher:srp_suites_anon() + ++ ssl_cipher:srp_suites(Version) + ++ ssl_cipher:srp_suites_anon(Version) ++ ssl_cipher:rc4_suites(Version), Supported1 = ssl_cipher:filter_suites(Supported0), Supported2 = [ssl_cipher_format:suite_bin_to_map(S) || S <- Supported1], @@ -2153,6 +2387,14 @@ protocol_version(Config, atom) -> tls_record:protocol_version(protocol_version(Config, tuple)) end. +protocol_versions(Config) -> + Version = protocol_version(Config), + case Version of + 'tlsv1.3' -> %% TLS-1.3 servers shall also support 1.2 + ['tlsv1.3', 'tlsv1.2']; + _ -> + [Version] + end. protocol_options(Config, Options) -> Protocol = proplists:get_value(protocol, Config, tls), {Protocol, Opts} = lists:keyfind(Protocol, 1, Options), @@ -2471,3 +2713,78 @@ digest() -> _ -> {digest, sha1} end. + +kill_openssl() -> + case os:type() of + {unix, _} -> + os:cmd("pkill openssl"); + {win32, _} -> + os:cmd("cmd.exe /C \"taskkill /IM openssl.exe /F\"") + end. + +hostname_format(Hostname) -> + case lists:member($., Hostname) of + true -> + Hostname; + false -> + "localhost" + end. + +erlang_ssl_receive_and_assert_negotiated_protocol(Socket, Protocol, Data) -> + case ssl:negotiated_protocol(Socket) of + {ok, Protocol} -> + active_recv(Socket, length(Data)); + Result -> + {error, {{expected, Protocol}, {got, Result}}} + end. + +check_openssl_npn_support(Config) -> + HelpText = os:cmd("openssl s_client --help"), + case string:str(HelpText, "nextprotoneg") of + 0 -> + {skip, "Openssl not compiled with nextprotoneg support"}; + _ -> + Config + end. + +new_config(PrivDir, ServerOpts0) -> + CaCertFile = proplists:get_value(cacertfile, ServerOpts0), + CertFile = proplists:get_value(certfile, ServerOpts0), + KeyFile = proplists:get_value(keyfile, ServerOpts0), + NewCaCertFile = filename:join(PrivDir, "new_ca.pem"), + NewCertFile = filename:join(PrivDir, "new_cert.pem"), + NewKeyFile = filename:join(PrivDir, "new_key.pem"), + file:copy(CaCertFile, NewCaCertFile), + file:copy(CertFile, NewCertFile), + file:copy(KeyFile, NewKeyFile), + ServerOpts1 = proplists:delete(cacertfile, ServerOpts0), + ServerOpts2 = proplists:delete(certfile, ServerOpts1), + ServerOpts = proplists:delete(keyfile, ServerOpts2), + + {ok, PEM} = file:read_file(NewCaCertFile), + ct:log("CA file content: ~p~n", [public_key:pem_decode(PEM)]), + + [{cacertfile, NewCaCertFile}, {certfile, NewCertFile}, + {keyfile, NewKeyFile} | ServerOpts]. + +sane_openssl_alpn_npn_renegotiate() -> + case os:cmd("openssl version") of + "LibreSSL 2.9.1" ++ _ -> + false; + "LibreSSL 2.6.4" ++ _ -> + false; + "OpenSSL 1.1.1a-freebsd" ++ _ -> + false; + _ -> + true + end. + +openssl_sane_dtls_alpn() -> + case os:cmd("openssl version") of + "OpenSSL 1.1.0g" ++ _ -> + false; + "OpenSSL 1.1.1a" ++ _ -> + false; + _-> + openssl_sane_dtls() + end. diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl deleted file mode 100644 index 07abddbcf7..0000000000 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ /dev/null @@ -1,2021 +0,0 @@ -%% -%% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2018. 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(ssl_to_openssl_SUITE). - -%% Note: This directive should only be used in test suites. --compile(export_all). - --include_lib("common_test/include/ct.hrl"). - --define(SLEEP, 1000). --define(OPENSSL_RENEGOTIATE, "R\n"). --define(OPENSSL_QUIT, "Q\n"). --define(OPENSSL_GARBAGE, "P\n"). --define(EXPIRE, 10). - -%%-------------------------------------------------------------------- -%% Common Test interface functions ----------------------------------- -%%-------------------------------------------------------------------- - -all() -> - case ssl_test_lib:openssl_sane_dtls() of - true -> - [{group, 'tlsv1.2'}, - {group, 'tlsv1.1'}, - {group, 'tlsv1'}, - {group, 'sslv3'}, - {group, 'dtlsv1.2'}, - {group, 'dtlsv1'}]; - false -> - [{group, 'tlsv1.2'}, - {group, 'tlsv1.1'}, - {group, 'tlsv1'}, - {group, 'sslv3'}] - end. - -groups() -> - case ssl_test_lib:openssl_sane_dtls() of - true -> - [{'tlsv1.2', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, - {'tlsv1.1', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, - {'tlsv1', [], all_versions_tests()++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, - {'sslv3', [], all_versions_tests()}, - {'dtlsv1.2', [], dtls_all_versions_tests()}, - {'dtlsv1', [], dtls_all_versions_tests()} - ]; - false -> - [{'tlsv1.2', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, - {'tlsv1.1', [], all_versions_tests() ++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, - {'tlsv1', [], all_versions_tests()++ alpn_tests() ++ npn_tests() ++ sni_server_tests()}, - {'sslv3', [], all_versions_tests()} - ] - end. - -all_versions_tests() -> - [ - erlang_client_openssl_server, - erlang_server_openssl_client, - erlang_client_openssl_server_dsa_cert, - erlang_server_openssl_client_dsa_cert, - erlang_client_openssl_server_anon, - erlang_server_openssl_client_anon, - erlang_server_openssl_client_anon_with_cert, - erlang_server_openssl_client_reuse_session, - erlang_client_openssl_server_renegotiate, - erlang_client_openssl_server_renegotiate_after_client_data, - erlang_client_openssl_server_nowrap_seqnum, - erlang_server_openssl_client_nowrap_seqnum, - erlang_client_openssl_server_no_server_ca_cert, - erlang_client_openssl_server_client_cert, - erlang_server_openssl_client_client_cert, - ciphers_rsa_signed_certs, - ciphers_dsa_signed_certs, - erlang_client_bad_openssl_server, - expired_session, - ssl2_erlang_server_openssl_client - ]. - -dtls_all_versions_tests() -> - case ssl_test_lib:openssl_sane_client_cert() of - true -> - [erlang_server_openssl_client_client_cert, - erlang_client_openssl_server_no_server_ca_cert, - erlang_client_openssl_server_client_cert - | dtls_all_versions_tests_2()]; - false -> - dtls_all_versions_tests_2() - end. - -dtls_all_versions_tests_2() -> - [erlang_client_openssl_server, - erlang_server_openssl_client, - erlang_client_openssl_server_dsa_cert, - erlang_server_openssl_client_dsa_cert, - erlang_client_openssl_server_anon, - erlang_server_openssl_client_anon, - erlang_server_openssl_client_anon_with_cert, - erlang_server_openssl_client_reuse_session, - erlang_client_openssl_server_renegotiate, - erlang_client_openssl_server_nowrap_seqnum, - erlang_server_openssl_client_nowrap_seqnum, - ciphers_rsa_signed_certs, - ciphers_dsa_signed_certs - %%expired_session - ]. - -alpn_tests() -> - [erlang_client_alpn_openssl_server_alpn, - erlang_server_alpn_openssl_client_alpn, - erlang_client_alpn_openssl_server, - erlang_client_openssl_server_alpn, - erlang_server_alpn_openssl_client, - erlang_server_openssl_client_alpn, - erlang_client_alpn_openssl_server_alpn_renegotiate, - erlang_server_alpn_openssl_client_alpn_renegotiate, - erlang_client_alpn_npn_openssl_server_alpn_npn, - erlang_server_alpn_npn_openssl_client_alpn_npn]. - -npn_tests() -> - [erlang_client_openssl_server_npn, - erlang_server_openssl_client_npn, - erlang_server_openssl_client_npn_renegotiate, - erlang_client_openssl_server_npn_renegotiate, - erlang_server_openssl_client_npn_only_client, - erlang_server_openssl_client_npn_only_server, - erlang_client_openssl_server_npn_only_client, - erlang_client_openssl_server_npn_only_server]. - -sni_server_tests() -> - [erlang_server_openssl_client_sni_match, - erlang_server_openssl_client_sni_match_fun, - erlang_server_openssl_client_sni_no_match, - erlang_server_openssl_client_sni_no_match_fun, - erlang_server_openssl_client_sni_no_header, - erlang_server_openssl_client_sni_no_header_fun]. - - -init_per_suite(Config0) -> - case os:find_executable("openssl") of - false -> - {skip, "Openssl not found"}; - _ -> - ct:pal("Version: ~p", [os:cmd("openssl version")]), - catch crypto:stop(), - try crypto:start() of - ok -> - ssl_test_lib:clean_start(), - Config = - case ssl_test_lib:openssl_dsa_support() of - true -> - Config1 = ssl_test_lib:make_rsa_cert(Config0), - ssl_test_lib:make_dsa_cert(Config1); - false -> - ssl_test_lib:make_rsa_cert(Config0) - end, - ssl_test_lib:cipher_restriction(Config) - catch _:_ -> - {skip, "Crypto did not start"} - end - end. - -end_per_suite(_Config) -> - ssl:stop(), - application:stop(crypto). - - -init_per_group(GroupName, Config) -> - case ssl_test_lib:is_tls_version(GroupName) of - true -> - case ssl_test_lib:supports_ssl_tls_version(GroupName) of - true -> - case ssl_test_lib:check_sane_openssl_version(GroupName) of - true -> - ssl_test_lib:init_tls_version(GroupName, Config); - false -> - {skip, openssl_does_not_support_version} - end; - false -> - {skip, openssl_does_not_support_version} - end; - _ -> - ssl:start(), - Config - end. - -end_per_group(GroupName, Config) -> - case ssl_test_lib:is_tls_version(GroupName) of - true -> - ssl_test_lib:clean_tls_version(Config); - false -> - Config - end. - -init_per_testcase(expired_session, Config) -> - ct:timetrap(?EXPIRE * 1000 * 5), - ssl:stop(), - application:load(ssl), - application:set_env(ssl, session_lifetime, ?EXPIRE), - ssl:start(), - Config; - -init_per_testcase(TestCase, Config) when - TestCase == ciphers_dsa_signed_certs; - TestCase == erlang_client_openssl_server_dsa_cert; - TestCase == erlang_server_openssl_client_dsa_cert; - TestCase == erlang_client_openssl_server_dsa_cert; - TestCase == erlang_server_openssl_client_dsa_cert -> - case ssl_test_lib:openssl_dsa_support() andalso ssl_test_lib:is_sane_oppenssl_client() of - true -> - special_init(TestCase, Config); - false -> - {skip, "DSA not supported by OpenSSL"} - end; -init_per_testcase(TestCase, Config) -> - ct:timetrap({seconds, 35}), - special_init(TestCase, Config). - -special_init(TestCase, Config) when - TestCase == ciphers_rsa_signed_certs; - TestCase == ciphers_dsa_signed_certs-> - ct:timetrap({seconds, 90}), - Config; -special_init(TestCase, Config) - when TestCase == erlang_client_openssl_server_renegotiate; - TestCase == erlang_client_openssl_server_nowrap_seqnum; - TestCase == erlang_server_openssl_client_nowrap_seqnum; - TestCase == erlang_client_openssl_server_renegotiate_after_client_data - -> - {ok, Version} = application:get_env(ssl, protocol_version), - check_sane_openssl_renegotaite(Config, Version); - -special_init(ssl2_erlang_server_openssl_client, Config) -> - case ssl_test_lib:supports_ssl_tls_version(sslv2) of - true -> - Config; - false -> - {skip, "sslv2 not supported by openssl"} - end; - -special_init(TestCase, Config) - when TestCase == erlang_client_alpn_openssl_server_alpn; - TestCase == erlang_server_alpn_openssl_client_alpn; - TestCase == erlang_client_alpn_openssl_server; - TestCase == erlang_client_openssl_server_alpn; - TestCase == erlang_server_alpn_openssl_client; - TestCase == erlang_server_openssl_client_alpn -> - check_openssl_alpn_support(Config); - -special_init(TestCase, Config) - when TestCase == erlang_client_alpn_openssl_server_alpn_renegotiate; - TestCase == erlang_server_alpn_openssl_client_alpn_renegotiate -> - {ok, Version} = application:get_env(ssl, protocol_version), - case check_sane_openssl_renegotaite(Config, Version) of - {skip, _} = Skip -> - Skip; - _ -> - check_openssl_alpn_support(Config) - end; - -special_init(TestCase, Config) - when TestCase == erlang_client_alpn_npn_openssl_server_alpn_npn; - TestCase == erlang_server_alpn_npn_openssl_client_alpn_npn -> - case check_openssl_alpn_support(Config) of - {skip, _} = Skip -> - Skip; - _ -> - check_openssl_npn_support(Config) - end; - -special_init(TestCase, Config) - when TestCase == erlang_client_openssl_server_npn; - TestCase == erlang_server_openssl_client_npn; - TestCase == erlang_server_openssl_client_npn_only_server; - TestCase == erlang_server_openssl_client_npn_only_client; - TestCase == erlang_client_openssl_server_npn_only_client; - TestCase == erlang_client_openssl_server_npn_only_server -> - check_openssl_npn_support(Config); - -special_init(TestCase, Config) - when TestCase == erlang_server_openssl_client_npn_renegotiate; - TestCase == erlang_client_openssl_server_npn_renegotiate -> - {ok, Version} = application:get_env(ssl, protocol_version), - case check_sane_openssl_renegotaite(Config, Version) of - {skip, _} = Skip -> - Skip; - _ -> - check_openssl_npn_support(Config) - end; - -special_init(TestCase, Config0) - when TestCase == erlang_server_openssl_client_sni_match; - TestCase == erlang_server_openssl_client_sni_no_match; - TestCase == erlang_server_openssl_client_sni_no_header; - TestCase == erlang_server_openssl_client_sni_match_fun; - TestCase == erlang_server_openssl_client_sni_no_match_fun; - TestCase == erlang_server_openssl_client_sni_no_header_fun -> - RsaOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config0), - Config = [{sni_server_opts, [{sni_hosts, - [{"a.server", [ - {certfile, proplists:get_value(certfile, RsaOpts)}, - {keyfile, proplists:get_value(keyfile, RsaOpts)} - ]}, - {"b.server", [ - {certfile, proplists:get_value(certfile, RsaOpts)}, - {keyfile, proplists:get_value(keyfile, RsaOpts)} - ]} - ]}]} | Config0], - check_openssl_sni_support(Config); -special_init(TestCase, Config) - when TestCase == erlang_server_openssl_client; - TestCase == erlang_server_openssl_client_client_cert; - TestCase == erlang_server_openssl_client_reuse_session -> - case ssl_test_lib:is_sane_oppenssl_client() of - true -> - Config; - false -> - {skip, "Broken OpenSSL client"} - end; -special_init(_, Config) -> - Config. - -end_per_testcase(reuse_session_expired, Config) -> - application:unset_env(ssl, session_lifetime), - Config; -end_per_testcase(_, Config) -> - Config. - -%%-------------------------------------------------------------------- -%% Test Cases -------------------------------------------------------- -%%-------------------------------------------------------------------- - -erlang_client_openssl_server() -> - [{doc,"Test erlang client with openssl server"}]. -erlang_client_openssl_server(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_test_lib:protocol_version(Config), - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-cert", CertFile, "-key", KeyFile], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, ClientOpts}]), - true = port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false). - -%%-------------------------------------------------------------------- -erlang_server_openssl_client() -> - [{doc,"Test erlang server with openssl client"}]. -erlang_server_openssl_client(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Version = ssl_test_lib:protocol_version(Config), - - Exe = "openssl", - Args = ["s_client", "-connect", hostname_format(Hostname) ++":" ++ integer_to_list(Port), - ssl_test_lib:version_flag(Version)], - - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - - true = port_command(OpenSslPort, Data), - - ssl_test_lib:check_result(Server, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - ssl_test_lib:close_port(OpenSslPort), - process_flag(trap_exit, false). - -erlang_client_openssl_server_dsa_cert() -> - [{doc,"Test erlang server with openssl client"}]. -erlang_client_openssl_server_dsa_cert(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ClientOpts = ssl_test_lib:ssl_options(client_dsa_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_dsa_verify_opts, Config), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CaCertFile = proplists:get_value(cacertfile, ServerOpts), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_test_lib:protocol_version(Config), - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-cert", CertFile, "-CAfile", CaCertFile, - "-key", KeyFile, "-Verify", "2", "-msg"], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, ClientOpts}]), - - true = port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. -%%-------------------------------------------------------------------- -erlang_server_openssl_client_dsa_cert() -> - [{doc,"Test erlang server with openssl client"}]. -erlang_server_openssl_client_dsa_cert(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ClientOpts = ssl_test_lib:ssl_options(client_dsa_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_dsa_verify_opts, Config), - - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - CaCertFile = proplists:get_value(cacertfile, ClientOpts), - CertFile = proplists:get_value(certfile, ClientOpts), - KeyFile = proplists:get_value(keyfile, ClientOpts), - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Version = ssl_test_lib:protocol_version(Config), - Exe = "openssl", - Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-cert", CertFile, - "-CAfile", CaCertFile, - "-key", KeyFile, "-msg"], - - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - true = port_command(OpenSslPort, Data), - - ssl_test_lib:check_result(Server, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - ssl_test_lib:close_port(OpenSslPort), - process_flag(trap_exit, false). - - %%-------------------------------------------------------------------- -erlang_client_openssl_server_anon() -> - [{doc,"Test erlang client with openssl server, anonymous"}]. -erlang_client_openssl_server_anon(Config) when is_list(Config) -> - process_flag(trap_exit, true), - %% OpenSSL expects a certificate and key, even if the cipher spec - %% is restructed to aNULL, so we use 'server_rsa_opts' here - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_anon_opts, Config), - VersionTuple = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl_test_lib:ecdh_dh_anonymous_suites(VersionTuple), - - case openssl_has_common_ciphers(Ciphers) of - false -> - {skip, not_supported_by_openssl}; - true -> - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_test_lib:protocol_version(Config), - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-cert", CertFile, "-key", KeyFile, - "-cipher", "aNULL", "-msg"], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, [{ciphers, Ciphers} | ClientOpts]}]), - - true = port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false) - end. -%%-------------------------------------------------------------------- -erlang_server_openssl_client_anon() -> - [{doc,"Test erlang server with openssl client, anonymous"}]. -erlang_server_openssl_client_anon(Config) when is_list(Config) -> - - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_anon_opts, Config), - VersionTuple = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl_test_lib:ecdh_dh_anonymous_suites(VersionTuple), - - case openssl_has_common_ciphers(Ciphers) of - false -> - {skip, not_supported_by_openssl}; - true -> - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, [{ciphers, Ciphers} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Version = ssl_test_lib:protocol_version(Config), - Exe = "openssl", - Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-cipher", "aNULL", "-msg"], - - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - true = port_command(OpenSslPort, Data), - - ssl_test_lib:check_result(Server, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - ssl_test_lib:close_port(OpenSslPort), - process_flag(trap_exit, false) - end. - -%%-------------------------------------------------------------------- -erlang_server_openssl_client_anon_with_cert() -> - [{doc,"Test erlang server with openssl client, anonymous (with cert)"}]. -erlang_server_openssl_client_anon_with_cert(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - VersionTuple = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl_test_lib:ecdh_dh_anonymous_suites(VersionTuple), - - case openssl_has_common_ciphers(Ciphers) of - false -> - {skip, not_supported_by_openssl}; - true -> - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, [{ciphers, Ciphers} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Version = ssl_test_lib:protocol_version(Config), - Exe = "openssl", - Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-cipher", "aNULL", "-msg"], - - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - true = port_command(OpenSslPort, Data), - - ssl_test_lib:check_result(Server, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - ssl_test_lib:close_port(OpenSslPort), - process_flag(trap_exit, false) - end. - - %%-------------------------------------------------------------------- -erlang_server_openssl_client_reuse_session() -> - [{doc, "Test erlang server with openssl client that reconnects with the" - "same session id, to test reusing of sessions."}]. -erlang_server_openssl_client_reuse_session(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {reconnect_times, 5}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Version = ssl_test_lib:protocol_version(Config), - - Exe = "openssl", - Args = ["s_client", "-connect", hostname_format(Hostname) - ++ ":" ++ integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-reconnect"], - - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - - true = port_command(OpenSslPort, Data), - - ssl_test_lib:check_result(Server, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - ssl_test_lib:close_port(OpenSslPort), - process_flag(trap_exit, false), - ok. - -%%-------------------------------------------------------------------- - -erlang_client_openssl_server_renegotiate() -> - [{doc,"Test erlang client when openssl server issuses a renegotiate"}]. -erlang_client_openssl_server_renegotiate(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - ErlData = "From erlang to openssl", - OpenSslData = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - CaCertFile = proplists:get_value(cacertfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_test_lib:protocol_version(Config), - - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-CAfile", CaCertFile, - "-cert", CertFile, "-key", KeyFile, "-msg"], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - delayed_send, [[ErlData, OpenSslData]]}}, - {options, ClientOpts}]), - - true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), - ct:sleep(?SLEEP), - true = port_command(OpensslPort, OpenSslData), - - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. -%%-------------------------------------------------------------------- -erlang_client_openssl_server_renegotiate_after_client_data() -> - [{doc,"Test erlang client when openssl server issuses a renegotiate after reading client data"}]. -erlang_client_openssl_server_renegotiate_after_client_data(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - ErlData = "From erlang to openssl", - OpenSslData = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CaCertFile = proplists:get_value(cacertfile, ServerOpts), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_test_lib:protocol_version(Config), - - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-CAfile", CaCertFile, - "-cert", CertFile, "-key", KeyFile, "-msg"], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - send_wait_send, [[ErlData, OpenSslData]]}}, - {options, ClientOpts}]), - - true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), - ct:sleep(?SLEEP), - true = port_command(OpensslPort, OpenSslData), - - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false), - ok. - -%%-------------------------------------------------------------------- - -erlang_client_openssl_server_nowrap_seqnum() -> - [{doc, "Test that erlang client will renegotiate session when", - "max sequence number celing is about to be reached. Although" - "in the testcase we use the test option renegotiate_at" - " to lower treashold substantially."}]. -erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - ErlData = "From erlang to openssl\n", - N = 10, - - Port = ssl_test_lib:inet_port(node()), - CaCertFile = proplists:get_value(cacertfile, ServerOpts), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_test_lib:protocol_version(Config), - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-CAfile", CaCertFile, - "-cert", CertFile, "-key", KeyFile, "-msg"], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, - trigger_renegotiate, [[ErlData, N+2]]}}, - {options, [{reuse_sessions, false}, - {renegotiate_at, N} | ClientOpts]}]), - - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false). -%%-------------------------------------------------------------------- -erlang_server_openssl_client_nowrap_seqnum() -> - [{doc, "Test that erlang client will renegotiate session when", - "max sequence number celing is about to be reached. Although" - "in the testcase we use the test option renegotiate_at" - " to lower treashold substantially."}]. -erlang_server_openssl_client_nowrap_seqnum(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - N = 10, - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, - trigger_renegotiate, [[Data, N+2]]}}, - {options, [{renegotiate_at, N}, {reuse_sessions, false} | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - Version = ssl_test_lib:protocol_version(Config), - Exe = "openssl", - Args = ["s_client","-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-msg"], - - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - - true = port_command(OpenSslPort, Data), - - ssl_test_lib:check_result(Server, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close(Server), - ssl_test_lib:close_port(OpenSslPort), - process_flag(trap_exit, false). - -%%-------------------------------------------------------------------- - -erlang_client_openssl_server_no_server_ca_cert() -> - [{doc, "Test erlang client when openssl server sends a cert chain not" - "including the ca cert. Explicitly test this even if it is" - "implicitly tested eleswhere."}]. -erlang_client_openssl_server_no_server_ca_cert(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_test_lib:protocol_version(Config), - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-cert", CertFile, "-key", KeyFile, "-msg"], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, ClientOpts}]), - - true = port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false). - -%%-------------------------------------------------------------------- -erlang_client_openssl_server_client_cert() -> - [{doc,"Test erlang client with openssl server when client sends cert"}]. -erlang_client_openssl_server_client_cert(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - CaCertFile = proplists:get_value(cacertfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_test_lib:protocol_version(Config), - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-cert", CertFile, "-CAfile", CaCertFile, - "-key", KeyFile, "-Verify", "2"], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, ClientOpts}]), - true = port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client), - process_flag(trap_exit, false). - -%%-------------------------------------------------------------------- -erlang_server_openssl_client_client_cert() -> - [{doc,"Test erlang server with openssl client when client sends cert"}]. -erlang_server_openssl_client_client_cert(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, - [{verify , verify_peer} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - CaCertFile = proplists:get_value(cacertfile, ClientOpts), - CertFile = proplists:get_value(certfile, ClientOpts), - KeyFile = proplists:get_value(keyfile, ClientOpts), - Version = ssl_test_lib:protocol_version(Config), - Exe = "openssl", - Args = ["s_client", "-cert", CertFile, - "-CAfile", CaCertFile, - "-key", KeyFile,"-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port), - ssl_test_lib:version_flag(Version)], - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - - true = port_command(OpenSslPort, Data), - ssl_test_lib:check_result(Server, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpenSslPort), - ssl_test_lib:close(Server), - process_flag(trap_exit, false). - -%%-------------------------------------------------------------------- -erlang_server_erlang_client_client_cert() -> - [{doc,"Test erlang server with erlang client when client sends cert"}]. -erlang_server_erlang_client_client_cert(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), - ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), - Version = ssl_test_lib:protocol_version(Config), - {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From erlang to erlang", - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, - %% Due to 1/n-1 splitting countermeasure Rizzo/Duong-Beast - [Data]}}, - {options, - [{verify , verify_peer} - | ServerOpts]}]), - Port = ssl_test_lib:inet_port(Server), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - %% Due to 1/n-1 splitting countermeasure Rizzo/Duong-Beast - {mfa, {ssl, send, [Data]}}, - {options, - [{versions, [Version]} | ClientOpts]}]), - - ssl_test_lib:check_result(Server, ok, Client, ok), - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - process_flag(trap_exit, false). - -%%-------------------------------------------------------------------- - -ciphers_rsa_signed_certs() -> - [{doc,"Test cipher suites that uses rsa certs"}]. -ciphers_rsa_signed_certs(Config) when is_list(Config) -> - Version = ssl_test_lib:protocol_version(Config), - Ciphers = ssl_test_lib:rsa_suites(openssl), - run_suites(Ciphers, Version, Config, rsa). -%%-------------------------------------------------------------------- - -ciphers_dsa_signed_certs() -> - [{doc,"Test cipher suites that uses dsa certs"}]. -ciphers_dsa_signed_certs(Config) when is_list(Config) -> - Version = ssl_test_lib:protocol_version(Config), - NVersion = ssl_test_lib:protocol_version(Config, tuple), - Ciphers = ssl_test_lib:dsa_suites(NVersion), - run_suites(Ciphers, Version, Config, dsa). - -%%-------------------------------------------------------------------- -erlang_client_bad_openssl_server() -> - [{doc,"Test what happens if openssl server sends garbage to erlang ssl client"}]. -erlang_client_bad_openssl_server(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_test_lib:protocol_version(Config), - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-cert", CertFile, "-key", KeyFile], - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, server_sent_garbage, []}}, - {options, - [{versions, [Version]} | ClientOpts]}]), - - %% Send garbage - true = port_command(OpensslPort, ?OPENSSL_GARBAGE), - - ct:sleep(?SLEEP), - - Client0 ! server_sent_garbage, - - ssl_test_lib:check_result(Client0, true), - - ssl_test_lib:close(Client0), - - %% Make sure openssl does not hang and leave zombie process - Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, no_result, []}}, - {options, - [{versions, [Version]} | ClientOpts]}]), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client1), - process_flag(trap_exit, false), - ok. - -%%-------------------------------------------------------------------- - -expired_session() -> - [{doc, "Test our ssl client handling of expired sessions. Will make" - "better code coverage of the ssl_manager module"}]. -expired_session(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), - "-cert", CertFile,"-key", KeyFile], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, tls), - - Client0 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - - ssl_test_lib:close(Client0), - - %% Make sure session is registered - ct:sleep(?SLEEP), - - Client1 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - - ssl_test_lib:close(Client1), - %% Make sure session is unregistered due to expiration - ct:sleep((?EXPIRE+1) * 1000), - - Client2 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - ssl_test_lib:close(Client2), - process_flag(trap_exit, false). - -%%-------------------------------------------------------------------- -ssl2_erlang_server_openssl_client() -> - [{doc,"Test that ssl v2 clients are rejected"}]. - -ssl2_erlang_server_openssl_client(Config) when is_list(Config) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, - {from, self()}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - - Exe = "openssl", - Args = ["s_client", "-connect", hostname_format(Hostname) ++ ":" ++ integer_to_list(Port), - "-ssl2", "-msg"], - - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ct:log("Ports ~p~n", [[erlang:port_info(P) || P <- erlang:ports()]]), - ssl_test_lib:consume_port_exit(OpenSslPort), - ssl_test_lib:check_server_alert(Server, unexpected_message), - process_flag(trap_exit, false). - -%%-------------------------------------------------------------------- - -erlang_client_alpn_openssl_server_alpn(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) -> - true = port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok) - end), - ok. - -%%-------------------------------------------------------------------- - -erlang_server_alpn_openssl_client_alpn(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) -> - true = port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok) - end), - ok. - -%%-------------------------------------------------------------------------- - -erlang_client_alpn_openssl_server(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_client_and_openssl_server_with_opts(Config, - [{alpn_advertised_protocols, [<<"spdy/2">>]}], - [], - Data, fun(Server, OpensslPort) -> - true = port_command(OpensslPort, Data), - ssl_test_lib:check_result(Server, ok) - end), - ok. - -%%-------------------------------------------------------------------------- - -erlang_client_openssl_server_alpn(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_client_and_openssl_server_with_opts(Config, - [], - ["-alpn", "spdy/2"], - Data, fun(Server, OpensslPort) -> - true = port_command(OpensslPort, Data), - ssl_test_lib:check_result(Server, ok) - end), - ok. - -%%-------------------------------------------------------------------------- - -erlang_server_alpn_openssl_client(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_server_and_openssl_client_with_opts(Config, - [{alpn_preferred_protocols, [<<"spdy/2">>]}], - [], - Data, fun(Server, OpensslPort) -> - true = port_command(OpensslPort, Data), - ssl_test_lib:check_result(Server, ok) - end), - ok. - -%%-------------------------------------------------------------------------- - -erlang_server_openssl_client_alpn(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_server_and_openssl_client_with_opts(Config, - [], - ["-alpn", "spdy/2"], - Data, fun(Server, OpensslPort) -> - true = port_command(OpensslPort, Data), - ssl_test_lib:check_result(Server, ok) - end), - ok. - -%%-------------------------------------------------------------------- - -erlang_client_alpn_openssl_server_alpn_renegotiate(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) -> - true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), - ct:sleep(?SLEEP), - true = port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok) - end), - ok. - -%%-------------------------------------------------------------------- - -erlang_server_alpn_openssl_client_alpn_renegotiate(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, fun(Client, OpensslPort) -> - true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), - ct:sleep(?SLEEP), - true = port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok) - end), - ok. - -%%-------------------------------------------------------------------- - -erlang_client_alpn_npn_openssl_server_alpn_npn(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_client_and_openssl_server_for_alpn_npn_negotiation(Config, Data, fun(Client, OpensslPort) -> - true = port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok) - end), - ok. - -%%-------------------------------------------------------------------- - -erlang_server_alpn_npn_openssl_client_alpn_npn(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_server_and_openssl_client_for_alpn_npn_negotiation(Config, Data, fun(Client, OpensslPort) -> - true = port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok) - end), - ok. - -%%-------------------------------------------------------------------- -erlang_client_openssl_server_npn() -> - [{doc,"Test erlang client with openssl server doing npn negotiation"}]. - -erlang_client_openssl_server_npn(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) -> - true = port_command(OpensslPort, Data), - - ssl_test_lib:check_result(Client, ok) - end), - ok. - -%%-------------------------------------------------------------------- -erlang_client_openssl_server_npn_renegotiate() -> - [{doc,"Test erlang client with openssl server doing npn negotiation and renegotiate"}]. - -erlang_client_openssl_server_npn_renegotiate(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, fun(Client, OpensslPort) -> - true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), - ct:sleep(?SLEEP), - true = port_command(OpensslPort, Data), - ssl_test_lib:check_result(Client, ok) - end), - ok. -%%-------------------------------------------------------------------------- -erlang_server_openssl_client_npn() -> - [{doc,"Test erlang server with openssl client and npn negotiation"}]. - -erlang_server_openssl_client_npn(Config) when is_list(Config) -> - - Data = "From openssl to erlang", - start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) -> - true = port_command(OpensslPort, Data), - ssl_test_lib:check_result(Server, ok) - end), - ok. - -%%-------------------------------------------------------------------------- -erlang_server_openssl_client_npn_renegotiate() -> - [{doc,"Test erlang server with openssl client and npn negotiation with renegotiation"}]. - -erlang_server_openssl_client_npn_renegotiate(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, fun(Server, OpensslPort) -> - true = port_command(OpensslPort, ?OPENSSL_RENEGOTIATE), - ct:sleep(?SLEEP), - true = port_command(OpensslPort, Data), - ssl_test_lib:check_result(Server, ok) - end), - ok. -%%-------------------------------------------------------------------------- -erlang_client_openssl_server_npn_only_server(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_client_and_openssl_server_with_opts(Config, [], - ["-nextprotoneg", "spdy/2"], Data, fun(Server, OpensslPort) -> - true = port_command(OpensslPort, Data), - ssl_test_lib:check_result(Server, ok) - end), - ok. - -%%-------------------------------------------------------------------------- - -erlang_client_openssl_server_npn_only_client(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_client_and_openssl_server_with_opts(Config, - [{client_preferred_next_protocols, - {client, [<<"spdy/2">>], <<"http/1.1">>}}], [], - Data, fun(Server, OpensslPort) -> - true = port_command(OpensslPort, Data), - ssl_test_lib:check_result(Server, ok) - end), - ok. - -%%-------------------------------------------------------------------------- -erlang_server_openssl_client_npn_only_server(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_server_and_openssl_client_with_opts(Config, [{next_protocols_advertised, [<<"spdy/2">>]}], [], - Data, fun(Server, OpensslPort) -> - true = port_command(OpensslPort, Data), - ssl_test_lib:check_result(Server, ok) - end), - ok. - -erlang_server_openssl_client_npn_only_client(Config) when is_list(Config) -> - Data = "From openssl to erlang", - start_erlang_server_and_openssl_client_with_opts(Config, [], ["-nextprotoneg", "spdy/2"], - Data, fun(Server, OpensslPort) -> - true = port_command(OpensslPort, Data), - ssl_test_lib:check_result(Server, ok) - end), - ok. -%-------------------------------------------------------------------------- -erlang_server_openssl_client_sni_no_header(Config) when is_list(Config) -> - erlang_server_openssl_client_sni_test(Config, undefined, undefined, "server Peer cert"). - -erlang_server_openssl_client_sni_no_header_fun(Config) when is_list(Config) -> - erlang_server_openssl_client_sni_test_sni_fun(Config, undefined, undefined, "server Peer cert"). - -erlang_server_openssl_client_sni_match(Config) when is_list(Config) -> - erlang_server_openssl_client_sni_test(Config, "a.server", "a.server", "server Peer cert"). - -erlang_server_openssl_client_sni_match_fun(Config) when is_list(Config) -> - erlang_server_openssl_client_sni_test_sni_fun(Config, "a.server", "a.server", "server Peer cert"). - -erlang_server_openssl_client_sni_no_match(Config) when is_list(Config) -> - erlang_server_openssl_client_sni_test(Config, "c.server", undefined, "server Peer cert"). - -erlang_server_openssl_client_sni_no_match_fun(Config) when is_list(Config) -> - erlang_server_openssl_client_sni_test_sni_fun(Config, "c.server", undefined, "server Peer cert"). - - -%%-------------------------------------------------------------------- -%% Internal functions ------------------------------------------------ -%%-------------------------------------------------------------------- -run_suites(Ciphers, Version, Config, Type) -> - {ClientOpts, ServerOpts} = - case Type of - rsa -> - {ssl_test_lib:ssl_options(client_rsa_opts, Config), - ssl_test_lib:ssl_options(server_rsa_opts, Config)}; - dsa -> - {ssl_test_lib:ssl_options(client_dsa_opts, Config), - ssl_test_lib:ssl_options(server_dsa_verify_opts, Config)} - end, - - Result = lists:map(fun(Cipher) -> - cipher(Cipher, Version, Config, ClientOpts, ServerOpts) end, - Ciphers), - case lists:flatten(Result) of - [] -> - ok; - Error -> - ct:log("Cipher suite errors: ~p~n", [Error]), - ct:fail(cipher_suite_failed_see_test_case_log) - end. - -client_read_check([], _Data) -> - ok; -client_read_check([Hd | T], Data) -> - case binary:match(Data, list_to_binary(Hd)) of - nomatch -> - nomatch; - _ -> - client_read_check(T, Data) - end. -client_check_result(Port, DataExpected, DataReceived) -> - receive - {Port, {data, TheData}} -> - Data = list_to_binary(TheData), - NewData = <<DataReceived/binary, Data/binary>>, - ct:log("New Data: ~p", [NewData]), - case client_read_check(DataExpected, NewData) of - ok -> - ok; - _ -> - client_check_result(Port, DataExpected, NewData) - end - after 20000 -> - ct:fail({"Time out on openSSL Client", {expected, DataExpected}, - {got, DataReceived}}) - end. -client_check_result(Port, DataExpected) -> - client_check_result(Port, DataExpected, <<"">>). - -send_and_hostname(SSLSocket) -> - ssl:send(SSLSocket, "OK"), - case ssl:connection_information(SSLSocket, [sni_hostname]) of - {ok, []} -> - undefined; - {ok, [{sni_hostname, Hostname}]} -> - Hostname - end. - -erlang_server_openssl_client_sni_test(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) -> - Version = ssl_test_lib:protocol_version(Config), - ct:log("Start running handshake, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]), - ServerOptions = proplists:get_value(sni_server_opts, Config) ++ proplists:get_value(server_rsa_opts, Config), - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, {mfa, {?MODULE, send_and_hostname, []}}, - {options, ServerOptions}]), - Port = ssl_test_lib:inet_port(Server), - Exe = "openssl", - ClientArgs = case SNIHostname of - undefined -> - openssl_client_args(Version, Hostname,Port); - _ -> - openssl_client_args(Version, Hostname, Port, SNIHostname) - end, - ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs), - - ssl_test_lib:check_result(Server, ExpectedSNIHostname), - ssl_test_lib:close_port(ClientPort), - ssl_test_lib:close(Server), - ok. - - -erlang_server_openssl_client_sni_test_sni_fun(Config, SNIHostname, ExpectedSNIHostname, ExpectedCN) -> - Version = ssl_test_lib:protocol_version(Config), - ct:log("Start running handshake for sni_fun, Config: ~p, SNIHostname: ~p, ExpectedSNIHostname: ~p, ExpectedCN: ~p", [Config, SNIHostname, ExpectedSNIHostname, ExpectedCN]), - [{sni_hosts, ServerSNIConf}] = proplists:get_value(sni_server_opts, Config), - SNIFun = fun(Domain) -> proplists:get_value(Domain, ServerSNIConf, undefined) end, - ServerOptions = proplists:get_value(server_rsa_opts, Config) ++ [{sni_fun, SNIFun}], - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, {mfa, {?MODULE, send_and_hostname, []}}, - {options, ServerOptions}]), - Port = ssl_test_lib:inet_port(Server), - Exe = "openssl", - ClientArgs = case SNIHostname of - undefined -> - openssl_client_args(Version, Hostname,Port); - _ -> - openssl_client_args(Version, Hostname, Port, SNIHostname) - end, - - ClientPort = ssl_test_lib:portable_open_port(Exe, ClientArgs), - - ssl_test_lib:check_result(Server, ExpectedSNIHostname), - ssl_test_lib:close_port(ClientPort), - ssl_test_lib:close(Server). - - -cipher(CipherSuite, Version, Config, ClientOpts, ServerOpts) -> - process_flag(trap_exit, true), - ct:log("Testing CipherSuite ~p~n", [CipherSuite]), - {ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - - Exe = "openssl", - Args = ["s_server", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-cert", CertFile, "-key", KeyFile], - - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - ConnectionInfo = {ok, {Version, CipherSuite}}, - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {ssl_test_lib, cipher_result, [ConnectionInfo]}}, - {options, - [{ciphers,[CipherSuite]} | - ClientOpts]}]), - - true = port_command(OpenSslPort, "Hello\n"), - - receive - {Port, {data, _}} when is_port(Port) -> - ok - after 500 -> - ct:log("Time out on openssl port, check that" - " the messages Hello and world are received" - " during close of port" , []), - ok - end, - - true = port_command(OpenSslPort, " world\n"), - - Result = ssl_test_lib:wait_for_result(Client, ok), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpenSslPort), - ssl_test_lib:close(Client), - - Return = case Result of - ok -> - []; - Error -> - [{CipherSuite, Error}] - end, - process_flag(trap_exit, false), - Return. - -start_erlang_client_and_openssl_server_with_opts(Config, ErlangClientOpts, OpensslServerOpts, Data, Callback) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ClientOpts = ErlangClientOpts ++ ClientOpts0, - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CaCertFile = proplists:get_value(cacertfile, ServerOpts), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_test_lib:protocol_version(Config), - - Exe = "openssl", - Args = case OpensslServerOpts of - [] -> - ["s_server", "-accept", - integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-CAfile", CaCertFile, - "-cert", CertFile,"-key", KeyFile]; - [Opt, Value] -> - ["s_server", Opt, Value, "-accept", - integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-CAfile", CaCertFile, - "-cert", CertFile,"-key", KeyFile] - end, - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive, [Data]}}, - {options, ClientOpts}]), - - Callback(Client, OpensslPort), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - - ssl_test_lib:close(Client), - process_flag(trap_exit, false). - -start_erlang_client_and_openssl_server_for_alpn_negotiation(Config, Data, Callback) -> - process_flag(trap_exit, true), - ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), - ClientOpts0 = proplists:get_value(client_rsa_verify_opts, Config), - ClientOpts = [{alpn_advertised_protocols, [<<"spdy/2">>]} | ClientOpts0], - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CaCertFile = proplists:get_value(cacertfile, ServerOpts), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_test_lib:protocol_version(Config), - - Exe = "openssl", - Args = ["s_server", "-msg", "-alpn", "http/1.1,spdy/2", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-CAfile", CaCertFile, - "-cert", CertFile, "-key", KeyFile], - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}}, - {options, ClientOpts}]), - - Callback(Client, OpensslPort), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - - ssl_test_lib:close(Client), - process_flag(trap_exit, false). - -start_erlang_server_and_openssl_client_for_alpn_negotiation(Config, Data, Callback) -> - process_flag(trap_exit, true), - ServerOpts0 = proplists:get_value(server_rsa_opts, Config), - ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>]} | ServerOpts0], - - {_, ServerNode, _} = ssl_test_lib:run_where(Config), - - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Version = ssl_test_lib:protocol_version(Config), - - Exe = "openssl", - Args = ["s_client", "-alpn", "http/1.0,spdy/2", "-msg", "-port", - integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-host", "localhost"], - - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - - Callback(Server, OpenSslPort), - - ssl_test_lib:close(Server), - - ssl_test_lib:close_port(OpenSslPort), - process_flag(trap_exit, false). - -start_erlang_client_and_openssl_server_for_alpn_npn_negotiation(Config, Data, Callback) -> - process_flag(trap_exit, true), - ServerOpts = proplists:get_value(server_rsa_opts, Config), - ClientOpts0 = proplists:get_value(client_rsa_opts, Config), - ClientOpts = [{alpn_advertised_protocols, [<<"spdy/2">>]}, - {client_preferred_next_protocols, {client, [<<"spdy/3">>, <<"http/1.1">>]}} | ClientOpts0], - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_test_lib:protocol_version(Config), - - Exe = "openssl", - Args = ["s_server", "-msg", "-alpn", "http/1.1,spdy/2", "-nextprotoneg", - "spdy/3", "-accept", integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-cert", CertFile, "-key", KeyFile], - - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}}, - {options, ClientOpts}]), - - Callback(Client, OpensslPort), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - - ssl_test_lib:close(Client), - process_flag(trap_exit, false). - -start_erlang_server_and_openssl_client_for_alpn_npn_negotiation(Config, Data, Callback) -> - process_flag(trap_exit, true), - ServerOpts0 = proplists:get_value(server_rsa_opts, Config), - ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>]}, - {next_protocols_advertised, [<<"spdy/3">>, <<"http/1.1">>]} | ServerOpts0], - - {_, ServerNode, _} = ssl_test_lib:run_where(Config), - - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Version = ssl_test_lib:protocol_version(Config), - Exe = "openssl", - Args = ["s_client", "-alpn", "http/1.1,spdy/2", "-nextprotoneg", "spdy/3", - "-msg", "-port", integer_to_list(Port), ssl_test_lib:version_flag(Version), - "-host", "localhost"], - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - - Callback(Server, OpenSslPort), - - ssl_test_lib:close(Server), - ssl_test_lib:close_port(OpenSslPort), - process_flag(trap_exit, false). - -start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callback) -> - process_flag(trap_exit, true), - ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), - ClientOpts = [{client_preferred_next_protocols, {client, [<<"spdy/2">>], <<"http/1.1">>}} | ClientOpts0], - - {ClientNode, _, Hostname} = ssl_test_lib:run_where(Config), - - Data = "From openssl to erlang", - - Port = ssl_test_lib:inet_port(node()), - CaCertFile = proplists:get_value(cacertfile, ServerOpts), - CertFile = proplists:get_value(certfile, ServerOpts), - KeyFile = proplists:get_value(keyfile, ServerOpts), - Version = ssl_test_lib:protocol_version(Config), - - Exe = "openssl", - Args = ["s_server", "-msg", "-nextprotoneg", "http/1.1,spdy/2", "-accept", integer_to_list(Port), - ssl_test_lib:version_flag(Version), - "-CAfile", CaCertFile, - "-cert", CertFile, "-key", KeyFile], - OpensslPort = ssl_test_lib:portable_open_port(Exe, Args), - - ssl_test_lib:wait_for_openssl_server(Port, proplists:get_value(protocol, Config)), - - Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, - {host, Hostname}, - {from, self()}, - {mfa, {?MODULE, - erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}}, - {options, ClientOpts}]), - - Callback(Client, OpensslPort), - - %% Clean close down! Server needs to be closed first !! - ssl_test_lib:close_port(OpensslPort), - - ssl_test_lib:close(Client), - process_flag(trap_exit, false). - -start_erlang_server_and_openssl_client_for_npn_negotiation(Config, Data, Callback) -> - process_flag(trap_exit, true), - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>]}, ServerOpts0], - - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive_and_assert_negotiated_protocol, [<<"spdy/2">>, Data]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Version = ssl_test_lib:protocol_version(Config), - - Exe = "openssl", - Args = ["s_client", "-nextprotoneg", "http/1.0,spdy/2", "-msg", "-connect", - hostname_format(Hostname) ++ ":" - ++ integer_to_list(Port), ssl_test_lib:version_flag(Version)], - - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - - Callback(Server, OpenSslPort), - - ssl_test_lib:close(Server), - - ssl_test_lib:close_port(OpenSslPort), - process_flag(trap_exit, false). - - -start_erlang_server_and_openssl_client_with_opts(Config, ErlangServerOpts, OpenSSLClientOpts, Data, Callback) -> - process_flag(trap_exit, true), - ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), - ServerOpts = ErlangServerOpts ++ ServerOpts0, - - {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - - - Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {?MODULE, erlang_ssl_receive, [Data]}}, - {options, ServerOpts}]), - Port = ssl_test_lib:inet_port(Server), - Version = ssl_test_lib:protocol_version(Config), - - Exe = "openssl", - Args = ["s_client"] ++ OpenSSLClientOpts ++ ["-msg", "-connect", - hostname_format(Hostname) ++ ":" ++ integer_to_list(Port), - ssl_test_lib:version_flag(Version)], - - OpenSslPort = ssl_test_lib:portable_open_port(Exe, Args), - - Callback(Server, OpenSslPort), - - ssl_test_lib:close(Server), - - ssl_test_lib:close_port(OpenSslPort), - process_flag(trap_exit, false). - - -erlang_ssl_receive_and_assert_negotiated_protocol(Socket, Protocol, Data) -> - {ok, Protocol} = ssl:negotiated_protocol(Socket), - erlang_ssl_receive(Socket, Data), - {ok, Protocol} = ssl:negotiated_protocol(Socket), - ok. - -erlang_ssl_receive(Socket, Data) -> - ct:log("Connection info: ~p~n", - [ssl:connection_information(Socket)]), - receive - {ssl, Socket, "R\n"} -> - %% Swallow s_client renegotiation command. - %% openssl s_client connected commands can appear on - %% server side with some openssl versions. - erlang_ssl_receive(Socket,Data); - {ssl, Socket, Data} -> - io:format("Received ~p~n",[Data]), - %% open_ssl server sometimes hangs waiting in blocking read - ssl:send(Socket, "Got it"), - ok; - {ssl, Socket, Byte} when length(Byte) == 1 -> - erlang_ssl_receive(Socket, tl(Data)); - {Port, {data,Debug}} when is_port(Port) -> - io:format("openssl ~s~n",[Debug]), - erlang_ssl_receive(Socket,Data); - Other -> - ct:fail({unexpected_message, Other}) - end. - -connection_info(Socket, Version) -> - case ssl:connection_information(Socket, [version]) of - {ok, [{version, Version}] = Info} -> - ct:log("Connection info: ~p~n", [Info]), - ok; - {ok, [{version, OtherVersion}]} -> - {wrong_version, OtherVersion} - end. - -connection_info_result(Socket) -> - ssl:connection_information(Socket). - - -delayed_send(Socket, [ErlData, OpenSslData]) -> - ct:sleep(?SLEEP), - ssl:send(Socket, ErlData), - erlang_ssl_receive(Socket, OpenSslData). - -server_sent_garbage(Socket) -> - receive - server_sent_garbage -> - {error, closed} == ssl:send(Socket, "data") - - end. - -send_wait_send(Socket, [ErlData, OpenSslData]) -> - ssl:send(Socket, ErlData), - ct:sleep(?SLEEP), - ssl:send(Socket, ErlData), - erlang_ssl_receive(Socket, OpenSslData). - -check_openssl_sni_support(Config) -> - HelpText = os:cmd("openssl s_client --help"), - case ssl_test_lib:is_sane_oppenssl_client() of - true -> - case string:str(HelpText, "-servername") of - 0 -> - {skip, "Current openssl doesn't support SNI"}; - _ -> - Config - end; - false -> - {skip, "Current openssl doesn't support SNI or extension handling is flawed"} - end. - - -check_openssl_npn_support(Config) -> - HelpText = os:cmd("openssl s_client --help"), - case string:str(HelpText, "nextprotoneg") of - 0 -> - {skip, "Openssl not compiled with nextprotoneg support"}; - _ -> - Config - end. - -check_openssl_alpn_support(Config) -> - HelpText = os:cmd("openssl s_client --help"), - case string:str(HelpText, "alpn") of - 0 -> - {skip, "Openssl not compiled with alpn support"}; - _ -> - Config - end. - -check_sane_openssl_renegotaite(Config, Version) when Version == 'tlsv1.1'; - Version == 'tlsv1.2' -> - case os:cmd("openssl version") of - "OpenSSL 1.0.1c" ++ _ -> - {skip, "Known renegotiation bug in OpenSSL"}; - "OpenSSL 1.0.1b" ++ _ -> - {skip, "Known renegotiation bug in OpenSSL"}; - "OpenSSL 1.0.1a" ++ _ -> - {skip, "Known renegotiation bug in OpenSSL"}; - "OpenSSL 1.0.1 " ++ _ -> - {skip, "Known renegotiation bug in OpenSSL"}; - _ -> - check_sane_openssl_renegotaite(Config) - end; -check_sane_openssl_renegotaite(Config, _) -> - check_sane_openssl_renegotaite(Config). - -check_sane_openssl_renegotaite(Config) -> - case os:cmd("openssl version") of - "OpenSSL 1.0.0" ++ _ -> - {skip, "Known renegotiation bug in OpenSSL"}; - "OpenSSL 0.9.8" ++ _ -> - {skip, "Known renegotiation bug in OpenSSL"}; - "OpenSSL 0.9.7" ++ _ -> - {skip, "Known renegotiation bug in OpenSSL"}; - _ -> - Config - end. - -workaround_openssl_s_clinent() -> - %% http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=683159 - %% https://bugs.archlinux.org/task/33919 - %% Bug seems to manifests it self if TLS version is not - %% explicitly specified - case os:cmd("openssl version") of - "OpenSSL 1.0.1c" ++ _ -> - ["-no_tls1_2"]; - "OpenSSL 1.0.1d" ++ _ -> - ["-no_tls1_2"]; - "OpenSSL 1.0.1e" ++ _ -> - ["-no_tls1_2"]; - "OpenSSL 1.0.1f" ++ _ -> - ["-no_tls1_2"]; - _ -> - [] - end. - -openssl_client_args(Version, Hostname, Port) -> - ["s_client", "-connect", Hostname ++ ":" ++ integer_to_list(Port), ssl_test_lib:version_flag(Version)]. - -openssl_client_args(Version, Hostname, Port, ServerName) -> - ["s_client", "-connect", Hostname ++ ":" ++ - integer_to_list(Port), ssl_test_lib:version_flag(Version), "-servername", ServerName]. - - -hostname_format(Hostname) -> - case lists:member($., Hostname) of - true -> - Hostname; - false -> - "localhost" - end. - - -openssl_has_common_ciphers(Ciphers) -> - OCiphers = ssl_test_lib:common_ciphers(openssl), - has_common_ciphers(Ciphers, OCiphers). - -has_common_ciphers([], _) -> - false; -has_common_ciphers([Cipher | Rest], OCiphers) -> - case lists:member(Cipher, OCiphers) of - true -> - true; - _ -> - has_common_ciphers(Rest, OCiphers) - end. diff --git a/lib/ssl/test/tls_1_3_record_SUITE.erl b/lib/ssl/test/tls_1_3_record_SUITE.erl new file mode 100644 index 0000000000..5df8853b1b --- /dev/null +++ b/lib/ssl/test/tls_1_3_record_SUITE.erl @@ -0,0 +1,973 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-2018. 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(tls_1_3_record_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("ssl/src/tls_record.hrl"). +-include_lib("ssl/src/tls_handshake.hrl"). +-include_lib("ssl/src/ssl_cipher.hrl"). +-include_lib("ssl/src/ssl_internal.hrl"). + +all() -> + [encode_decode, + finished_verify_data, + '1_RTT_handshake']. + +init_per_suite(Config) -> + catch crypto:stop(), + try (ok == crypto:start()) andalso ssl_test_lib:sufficient_crypto_support('tlsv1.3') of + true -> + ssl_test_lib:clean_start(), + Config; + false -> + {skip, "Not enough crypto support for TLS-1.3"} + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:unload(ssl), + application:stop(crypto). +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +encode_decode() -> + [{doc,"Test TLS 1.3 record encode/decode functions"}]. + +encode_decode(_Config) -> + ConnectionStates = + #{current_read => + #{beast_mitigation => one_n_minus_one, + cipher_state => + {cipher_state, + <<14,172,111,243,199,170,242,203,126,205,34,93,122,115,226,14, + 15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>, + <<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228, + 131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>, + undefined,undefined,undefined,16}, + client_verify_data => undefined,compression_state => undefined, + mac_secret => undefined,secure_renegotiation => undefined, + security_parameters => + {security_parameters, + <<19,2>>, + 0,8,2,undefined,undefined,undefined,undefined,undefined, + sha384,undefined,undefined, + {handshake_secret, + <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121, + 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218, + 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56, + 157>>}, + undefined, + <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207, + 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>, + undefined}, + sequence_number => 0,server_verify_data => undefined}, + current_write => + #{beast_mitigation => one_n_minus_one, + cipher_state => + {cipher_state, + <<14,172,111,243,199,170,242,203,126,205,34,93,122,115,226,14, + 15,117,155,48,24,112,61,15,113,208,127,51,179,227,194,232>>, + <<197,54,168,218,54,91,157,58,30,201,197,142,51,58,53,231,228, + 131,57,122,170,78,82,196,30,48,23,16,95,255,185,236>>, + undefined,undefined,undefined,16}, + client_verify_data => undefined,compression_state => undefined, + mac_secret => undefined,secure_renegotiation => undefined, + security_parameters => + {security_parameters, + <<19,2>>, + 0,8,2,undefined,undefined,undefined,undefined,undefined, + sha384,undefined,undefined, + {handshake_secret, + <<128,229,186,211,62,127,182,20,62,166,233,23,135,64,121, + 3,104,251,214,161,253,31,3,2,232,37,8,221,189,72,64,218, + 121,41,112,148,254,34,68,164,228,60,161,201,132,55,56, + 157>>}, + undefined, + <<92,24,205,75,244,60,136,212,250,32,214,20,37,3,213,87,61,207, + 147,61,168,145,177,118,160,153,33,53,48,108,191,174>>, + undefined}, + sequence_number => 0,server_verify_data => undefined}}, + + PlainText = [11, + <<0,2,175>>, + <<0,0,2,171,0,2,166,48,130,2,162,48,130,1,138,2,9,0,186,57,220,137,88,255, + 191,235,48,13,6,9,42,134,72,134,247,13,1,1,11,5,0,48,18,49,16,48,14,6,3,85, + 4,3,12,7,84,101,115,116,32,67,65,48,30,23,13,49,56,48,53,48,52,49,52,49,50, + 51,56,90,23,13,50,56,48,50,48,52,49,52,49,50,51,56,90,48,20,49,18,48,16,6, + 3,85,4,3,12,9,108,111,99,97,108,104,111,115,116,48,130,1,34,48,13,6,9,42, + 134,72,134,247,13,1,1,1,5,0,3,130,1,15,0,48,130,1,10,2,130,1,1,0,169,40, + 144,176,121,63,134,97,144,126,243,183,225,157,37,131,183,225,87,243,23,88, + 230,70,9,134,32,147,7,27,167,98,51,81,224,75,199,12,229,251,195,207,75,179, + 181,78,128,3,255,44,58,39,43,172,142,45,186,58,51,65,187,199,154,153,245, + 70,133,137,1,27,87,42,116,65,251,129,109,145,233,97,171,71,54,213,185,74, + 209,166,11,218,189,119,206,86,170,60,212,213,85,189,30,50,215,23,185,53, + 132,238,132,176,198,250,139,251,198,221,225,128,109,113,23,220,39,143,71, + 30,59,189,51,244,61,158,214,146,180,196,103,169,189,221,136,78,129,216,148, + 2,9,8,65,37,224,215,233,13,209,21,235,20,143,33,74,59,53,208,90,152,94,251, + 54,114,171,39,88,230,227,158,211,135,37,182,67,205,161,59,20,138,58,253,15, + 53,48,8,157,9,95,197,9,177,116,21,54,9,125,78,109,182,83,20,16,234,223,116, + 41,155,123,87,77,17,120,153,246,239,124,130,105,219,166,146,242,151,66,198, + 75,72,63,28,246,86,16,244,223,22,36,50,15,247,222,98,6,152,136,154,72,150, + 73,127,2,3,1,0,1,48,13,6,9,42,134,72,134,247,13,1,1,11,5,0,3,130,1,1,0,76, + 33,54,160,229,219,219,193,150,116,245,252,18,39,235,145,86,12,167,171,52, + 117,166,30,83,5,216,245,177,217,247,95,1,136,94,246,212,108,248,230,111, + 225,202,189,6,129,8,70,128,245,18,204,215,87,82,129,253,227,122,66,182,184, + 189,30,193,169,144,218,216,109,105,110,215,144,60,104,162,178,101,164,218, + 122,60,37,41,143,57,150,52,59,51,112,238,113,239,168,114,69,183,143,154,73, + 61,58,80,247,172,95,251,55,28,186,28,200,206,230,118,243,92,202,189,49,76, + 124,252,76,0,247,112,85,194,69,59,222,163,228,103,49,110,104,109,251,155, + 138,9,37,167,49,189,48,134,52,158,185,129,24,96,153,196,251,90,206,76,239, + 175,119,174,165,133,108,222,125,237,125,187,149,152,83,190,16,202,94,202, + 201,40,218,22,254,63,189,41,174,97,140,203,70,18,196,118,237,175,134,79,78, + 246,2,61,54,77,186,112,32,17,193,192,188,217,252,215,200,7,245,180,179,132, + 183,212,229,155,15,152,206,135,56,81,88,3,123,244,149,110,182,72,109,70,62, + 146,152,146,151,107,126,216,210,9,93,0,0>>], + + {[_Header|Encoded], _} = tls_record_1_3:encode_plain_text(22, PlainText, ConnectionStates), + CipherText = #ssl_tls{type = 23, version = {3,3}, fragment = Encoded}, + + {#ssl_tls{type = 22, version = {3,4}, fragment = DecodedText}, _} = + tls_record_1_3:decode_cipher_text(CipherText, ConnectionStates), + + DecodedText = iolist_to_binary(PlainText), + ct:log("Decoded: ~p ~n", [DecodedText]), + ok. +%%-------------------------------------------------------------------- +'1_RTT_handshake'() -> + [{doc,"Test TLS 1.3 1-RTT Handshake"}]. + +'1_RTT_handshake'(_Config) -> + %% ConnectionStates with NULL cipher + ConnStatesNull = + #{current_write => + #{security_parameters => + #security_parameters{cipher_suite = ?TLS_NULL_WITH_NULL_NULL}, + sequence_number => 0 + } + }, + + %% {client} construct a ClientHello handshake message: + %% + %% ClientHello (196 octets): 01 00 00 c0 03 03 cb 34 ec b1 e7 81 63 + %% ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 + %% 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b + %% 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 + %% 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23 + %% 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2 + %% 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a + %% af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 + %% 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 + %% 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 + %% + %% {client} send handshake record: + %% + %% payload (196 octets): 01 00 00 c0 03 03 cb 34 ec b1 e7 81 63 ba + %% 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 02 + %% 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b 00 + %% 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 + %% 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23 00 + %% 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2 3d + %% 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af + %% 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 + %% 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 + %% 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 + %% + %% complete record (201 octets): 16 03 01 00 c4 01 00 00 c0 03 03 cb + %% 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 + %% ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 + %% 00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 + %% 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 + %% 01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d + %% e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d + %% 54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e + %% 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 + %% 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 + ClientHello = + hexstr2bin("01 00 00 c0 03 03 cb 34 ec b1 e7 81 63 + ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 ec 18 a2 ef 62 83 + 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 00 91 00 00 00 0b + 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 + 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 23 + 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d e5 60 e4 bd 43 d2 + 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a + af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 + 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 + 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"), + + ClientHelloRecord = + %% Current implementation always sets + %% legacy_record_version to Ox0303 + hexstr2bin("16 03 03 00 c4 01 00 00 c0 03 03 cb + 34 ec b1 e7 81 63 ba 1c 38 c6 da cb 19 6a 6d ff a2 1a 8d 99 12 + ec 18 a2 ef 62 83 02 4d ec e7 00 00 06 13 01 13 03 13 02 01 00 + 00 91 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 + 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 + 01 03 01 04 00 23 00 00 00 33 00 26 00 24 00 1d 00 20 99 38 1d + e5 60 e4 bd 43 d2 3d 8e 43 5a 7d ba fe b3 c0 6e 51 c1 3c ae 4d + 54 13 69 1e 52 9a af 2c 00 2b 00 03 02 03 04 00 0d 00 20 00 1e + 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 + 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01"), + + {CHEncrypted, _} = + tls_record:encode_handshake(ClientHello, {3,4}, ConnStatesNull), + ClientHelloRecord = iolist_to_binary(CHEncrypted), + + %% {server} extract secret "early": + %% + %% salt: 0 (all zero octets) + %% + %% IKM (32 octets): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + %% 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + %% + %% secret (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c + %% e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a + HKDFAlgo = sha256, + Salt = binary:copy(<<?BYTE(0)>>, 32), + IKM = binary:copy(<<?BYTE(0)>>, 32), + EarlySecret = + hexstr2bin("33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c + e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a"), + + {early_secret, EarlySecret} = tls_v1:key_schedule(early_secret, HKDFAlgo, {psk, Salt}), + + %% {client} create an ephemeral x25519 key pair: + %% + %% private key (32 octets): 49 af 42 ba 7f 79 94 85 2d 71 3e f2 78 + %% 4b cb ca a7 91 1d e2 6a dc 56 42 cb 63 45 40 e7 ea 50 05 + %% + %% public key (32 octets): 99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d + %% ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c + CPublicKey = + hexstr2bin("99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d + ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c"), + + %% {server} create an ephemeral x25519 key pair: + %% + %% private key (32 octets): b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56 + %% 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e + %% + %% public key (32 octets): c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 + %% 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f + SPrivateKey = + hexstr2bin("b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56 + 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e"), + + SPublicKey = + hexstr2bin("c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 + 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f"), + + %% {server} construct a ServerHello handshake message: + %% + %% ServerHello (90 octets): 02 00 00 56 03 03 a6 af 06 a4 12 18 60 + %% dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e + %% d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88 + %% 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 + %% dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04 + ServerHello = + hexstr2bin("02 00 00 56 03 03 a6 af 06 a4 12 18 60 + dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e + d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88 + 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 + dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"), + + %% {server} derive secret for handshake "tls13 derived": + %% + %% PRK (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c e2 + %% 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a + %% + %% hash (32 octets): e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 + %% 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55 + %% + %% info (49 octets): 00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64 + %% 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 + %% 64 9b 93 4c a4 95 99 1b 78 52 b8 55 + %% + %% expanded (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba + %% b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba + Hash = + hexstr2bin("e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 + 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55"), + + Hash = crypto:hash(HKDFAlgo, <<>>), + + Info = + hexstr2bin("00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64 + 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 + 64 9b 93 4c a4 95 99 1b 78 52 b8 55"), + + Info = tls_v1:create_info(<<"derived">>, Hash, ssl_cipher:hash_size(HKDFAlgo)), + + Expanded = + hexstr2bin("6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba + b6 97 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba"), + + Expanded = tls_v1:derive_secret(EarlySecret, <<"derived">>, <<>>, HKDFAlgo), + + %% {server} extract secret "handshake": + %% + %% salt (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba b6 97 + %% 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba + %% + %% IKM (32 octets): 8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d + %% 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d + %% + %% secret (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b + %% 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac + + %% salt = Expanded + HandshakeIKM = + hexstr2bin("8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d + 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d"), + + HandshakeSecret = + hexstr2bin("1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b + 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac"), + + HandshakeIKM = crypto:compute_key(ecdh, CPublicKey, SPrivateKey, x25519), + + {handshake_secret, HandshakeSecret} = + tls_v1:key_schedule(handshake_secret, HKDFAlgo, HandshakeIKM, + {early_secret, EarlySecret}), + + %% {server} derive secret "tls13 c hs traffic": + %% + %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 + %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac + %% + %% hash (32 octets): 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed + %% d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 + %% + %% info (54 octets): 00 20 12 74 6c 73 31 33 20 63 20 68 73 20 74 72 + %% 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 + %% ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 + %% + %% expanded (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e + %% 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21 + + %% PRK = HandshakeSecret + CHSTHash = + hexstr2bin("86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed + d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"), + + CHSTInfo = + hexstr2bin("00 20 12 74 6c 73 31 33 20 63 20 68 73 20 74 72 + 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 + ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"), + + CHSTrafficSecret = + hexstr2bin(" b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e + 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21"), + + CHSH = <<ClientHello/binary,ServerHello/binary>>, + CHSTHash = crypto:hash(HKDFAlgo, CHSH), + CHSTInfo = tls_v1:create_info(<<"c hs traffic">>, CHSTHash, ssl_cipher:hash_size(HKDFAlgo)), + + CHSTrafficSecret = + tls_v1:client_handshake_traffic_secret(HKDFAlgo, {handshake_secret, HandshakeSecret}, CHSH), + + %% {server} derive secret "tls13 s hs traffic": + %% + %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 + %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac + %% + %% hash (32 octets): 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed + %% d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 + %% + %% info (54 octets): 00 20 12 74 6c 73 31 33 20 73 20 68 73 20 74 72 + %% 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 + %% ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 + %% + %% expanded (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d + %% 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 + + %% PRK = HandshakeSecret + %% hash = CHSTHash + SHSTInfo = + hexstr2bin("00 20 12 74 6c 73 31 33 20 73 20 68 73 20 74 72 + 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 + ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"), + + SHSTrafficSecret = + hexstr2bin("b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d + 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38"), + + SHSTInfo = tls_v1:create_info(<<"s hs traffic">>, CHSTHash, ssl_cipher:hash_size(HKDFAlgo)), + + SHSTrafficSecret = + tls_v1:server_handshake_traffic_secret(HKDFAlgo, {handshake_secret, HandshakeSecret}, CHSH), + + + %% {server} derive secret for master "tls13 derived": + %% + %% PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 + %% 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac + %% + %% hash (32 octets): e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 + %% 27 ae 41 e4 64 9b 93 4c a4 95 99 1b 78 52 b8 55 + %% + %% info (49 octets): 00 20 0d 74 6c 73 31 33 20 64 65 72 69 76 65 64 + %% 20 e3 b0 c4 42 98 fc 1c 14 9a fb f4 c8 99 6f b9 24 27 ae 41 e4 + %% 64 9b 93 4c a4 95 99 1b 78 52 b8 55 + %% + %% expanded (32 octets): 43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 + %% 90 b5 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4 + + %% PRK = HandshakeSecret + %% hash = Hash + %% info = Info + MasterDeriveSecret = + hexstr2bin("43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 + 90 b5 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4"), + + MasterDeriveSecret = tls_v1:derive_secret(HandshakeSecret, <<"derived">>, <<>>, HKDFAlgo), + + %% {server} extract secret "master": + %% + %% salt (32 octets): 43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 90 b5 + %% 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4 + %% + %% IKM (32 octets): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + %% 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + %% + %% secret (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a + %% 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + + %% salt = MasterDeriveSecret + %% IKM = IKM + MasterSecret = + hexstr2bin("18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a + 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19"), + + {master_secret, MasterSecret} = + tls_v1:key_schedule(master_secret, HKDFAlgo, {handshake_secret, HandshakeSecret}), + + %% {server} send handshake record: + %% + %% payload (90 octets): 02 00 00 56 03 03 a6 af 06 a4 12 18 60 dc 5e + %% 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 34 da c1 55 77 2e d3 e2 + %% 69 28 00 13 01 00 00 2e 00 33 00 24 00 1d 00 20 c9 82 88 76 11 + %% 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 + %% b1 b0 4e 75 1f 0f 00 2b 00 02 03 04 + %% + %% complete record (95 octets): 16 03 03 00 5a 02 00 00 56 03 03 a6 + %% af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 + %% 34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 + %% 1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 + %% cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04 + + %% payload = ServerHello + ServerHelloRecord = + hexstr2bin("16 03 03 00 5a 02 00 00 56 03 03 a6 + af 06 a4 12 18 60 dc 5e 6e 60 24 9c d3 4c 95 93 0c 8a c5 cb 14 + 34 da c1 55 77 2e d3 e2 69 28 00 13 01 00 00 2e 00 33 00 24 00 + 1d 00 20 c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 72 e1 56 d6 + cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f 00 2b 00 02 03 04"), + + {SHEncrypted, _} = + tls_record:encode_handshake(ServerHello, {3,4}, ConnStatesNull), + ServerHelloRecord = iolist_to_binary(SHEncrypted), + + %% {server} derive write traffic keys for handshake data: + %% + %% PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 + %% e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 + %% + %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + %% + %% key expanded (16 octets): 3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e + %% e4 03 bc + %% + %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + %% + %% iv expanded (12 octets): 5d 31 3e b2 67 12 76 ee 13 00 0b 30 + + %% PRK = SHSTrafficSecret + WriteKeyInfo = + hexstr2bin("00 10 09 74 6c 73 31 33 20 6b 65 79 00"), + + WriteKey = + hexstr2bin("3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e e4 03 bc"), + + WriteIVInfo = + hexstr2bin("00 0c 08 74 6c 73 31 33 20 69 76 00"), + + WriteIV = + hexstr2bin(" 5d 31 3e b2 67 12 76 ee 13 00 0b 30"), + + Cipher = aes_128_gcm, %% TODO: get from ServerHello + + WriteKeyInfo = tls_v1:create_info(<<"key">>, <<>>, ssl_cipher:key_material(Cipher)), + %% TODO: remove hardcoded IV size + WriteIVInfo = tls_v1:create_info(<<"iv">>, <<>>, 12), + + {WriteKey, WriteIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, SHSTrafficSecret), + + %% {server} construct an EncryptedExtensions handshake message: + %% + %% EncryptedExtensions (40 octets): 08 00 00 24 00 22 00 0a 00 14 00 + %% 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c + %% 00 02 40 01 00 00 00 00 + %% + %% {server} construct a Certificate handshake message: + %% + %% Certificate (445 octets): 0b 00 01 b9 00 00 01 b5 00 01 b0 30 82 + %% 01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48 + %% 86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03 + %% 72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17 + %% 0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06 + %% 03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7 + %% 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f + %% 82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26 + %% d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c + %% 1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52 + %% 4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74 + %% 80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93 + %% ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03 + %% 01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06 + %% 03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01 + %% 01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a + %% 72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea + %% e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01 + %% 51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be + %% c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b + %% 1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8 + %% 96 12 29 ac 91 87 b4 2b 4d e1 00 00 + %% + %% {server} construct a CertificateVerify handshake message: + %% + %% CertificateVerify (136 octets): 0f 00 00 84 08 04 00 80 5a 74 7c + %% 5d 88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a + %% b3 ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07 + %% 86 53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b + %% be 8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44 + %% 5c 9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a + %% 3d a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3 + EncryptedExtensions = + hexstr2bin("08 00 00 24 00 22 00 0a 00 14 00 + 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 1c + 00 02 40 01 00 00 00 00"), + + Certificate = + hexstr2bin("0b 00 01 b9 00 00 01 b5 00 01 b0 30 82 + 01 ac 30 82 01 15 a0 03 02 01 02 02 01 02 30 0d 06 09 2a 86 48 + 86 f7 0d 01 01 0b 05 00 30 0e 31 0c 30 0a 06 03 55 04 03 13 03 + 72 73 61 30 1e 17 0d 31 36 30 37 33 30 30 31 32 33 35 39 5a 17 + 0d 32 36 30 37 33 30 30 31 32 33 35 39 5a 30 0e 31 0c 30 0a 06 + 03 55 04 03 13 03 72 73 61 30 81 9f 30 0d 06 09 2a 86 48 86 f7 + 0d 01 01 01 05 00 03 81 8d 00 30 81 89 02 81 81 00 b4 bb 49 8f + 82 79 30 3d 98 08 36 39 9b 36 c6 98 8c 0c 68 de 55 e1 bd b8 26 + d3 90 1a 24 61 ea fd 2d e4 9a 91 d0 15 ab bc 9a 95 13 7a ce 6c + 1a f1 9e aa 6a f9 8c 7c ed 43 12 09 98 e1 87 a8 0e e0 cc b0 52 + 4b 1b 01 8c 3e 0b 63 26 4d 44 9a 6d 38 e2 2a 5f da 43 08 46 74 + 80 30 53 0e f0 46 1c 8c a9 d9 ef bf ae 8e a6 d1 d0 3e 2b d1 93 + ef f0 ab 9a 80 02 c4 74 28 a6 d3 5a 8d 88 d7 9f 7f 1e 3f 02 03 + 01 00 01 a3 1a 30 18 30 09 06 03 55 1d 13 04 02 30 00 30 0b 06 + 03 55 1d 0f 04 04 03 02 05 a0 30 0d 06 09 2a 86 48 86 f7 0d 01 + 01 0b 05 00 03 81 81 00 85 aa d2 a0 e5 b9 27 6b 90 8c 65 f7 3a + 72 67 17 06 18 a5 4c 5f 8a 7b 33 7d 2d f7 a5 94 36 54 17 f2 ea + e8 f8 a5 8c 8f 81 72 f9 31 9c f3 6b 7f d6 c5 5b 80 f2 1a 03 01 + 51 56 72 60 96 fd 33 5e 5e 67 f2 db f1 02 70 2e 60 8c ca e6 be + c1 fc 63 a4 2a 99 be 5c 3e b7 10 7c 3c 54 e9 b9 eb 2b d5 20 3b + 1c 3b 84 e0 a8 b2 f7 59 40 9b a3 ea c9 d9 1d 40 2d cc 0c c8 f8 + 96 12 29 ac 91 87 b4 2b 4d e1 00 00"), + + CertificateVerify = + hexstr2bin("0f 00 00 84 08 04 00 80 5a 74 7c + 5d 88 fa 9b d2 e5 5a b0 85 a6 10 15 b7 21 1f 82 4c d4 84 14 5a + b3 ff 52 f1 fd a8 47 7b 0b 7a bc 90 db 78 e2 d3 3a 5c 14 1a 07 + 86 53 fa 6b ef 78 0c 5e a2 48 ee aa a7 85 c4 f3 94 ca b6 d3 0b + be 8d 48 59 ee 51 1f 60 29 57 b1 54 11 ac 02 76 71 45 9e 46 44 + 5c 9e a5 8c 18 1e 81 8e 95 b8 c3 fb 0b f3 27 84 09 d3 be 15 2a + 3d a5 04 3e 06 3d da 65 cd f5 ae a2 0d 53 df ac d4 2f 74 f3"), + + %% {server} calculate finished "tls13 finished": + %% + %% PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 + %% e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 + %% + %% hash (0 octets): (empty) + %% + %% info (18 octets): 00 20 0e 74 6c 73 31 33 20 66 69 6e 69 73 68 65 + %% 64 00 + %% + %% expanded (32 octets): 00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85 + %% c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8 + %% + %% finished (32 octets): 9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4 + %% de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18 + + %% PRK = SHSTrafficSecret + FInfo = + hexstr2bin("00 20 0e 74 6c 73 31 33 20 66 69 6e 69 73 68 65 + 64 00"), + + FExpanded = + hexstr2bin("00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85 + c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8"), + + FinishedVerifyData = + hexstr2bin("9b 9b 14 1d 90 63 37 fb d2 cb dc e7 1d f4 + de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 18"), + + FInfo = tls_v1:create_info(<<"finished">>, <<>>, ssl_cipher:hash_size(HKDFAlgo)), + + FExpanded = tls_v1:finished_key(SHSTrafficSecret, HKDFAlgo), + + MessageHistory0 = [CertificateVerify, + Certificate, + EncryptedExtensions, + ServerHello, + ClientHello], + + FinishedVerifyData = tls_v1:finished_verify_data(FExpanded, HKDFAlgo, MessageHistory0), + + %% {server} construct a Finished handshake message: + %% + %% Finished (36 octets): 14 00 00 20 9b 9b 14 1d 90 63 37 fb d2 cb + %% dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 + %% 18 + FinishedHSBin = + hexstr2bin("14 00 00 20 9b 9b 14 1d 90 63 37 fb d2 cb + dc e7 1d f4 de da 4a b4 2c 30 95 72 cb 7f ff ee 54 54 b7 8f 07 + 18"), + + FinishedHS = #finished{verify_data = FinishedVerifyData}, + + FinishedIOList = tls_handshake:encode_handshake(FinishedHS, {3,4}), + FinishedHSBin = iolist_to_binary(FinishedIOList), + + %% {server} derive secret "tls13 c ap traffic": + %% + %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 + %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + %% + %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a + %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + %% + %% info (54 octets): 00 20 12 74 6c 73 31 33 20 63 20 61 70 20 74 72 + %% 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b + %% 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + %% + %% expanded (32 octets): 9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce + %% 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5 + + %% PRK = MasterSecret + CAPTHash = + hexstr2bin("96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a + 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"), + CAPTInfo = + hexstr2bin("00 20 12 74 6c 73 31 33 20 63 20 61 70 20 74 72 + 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b + 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"), + + CAPTrafficSecret = + hexstr2bin("9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce + 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5"), + + CHSF = <<ClientHello/binary, + ServerHello/binary, + EncryptedExtensions/binary, + Certificate/binary, + CertificateVerify/binary, + FinishedHSBin/binary>>, + + CAPTHash = crypto:hash(HKDFAlgo, CHSF), + + CAPTInfo = + tls_v1:create_info(<<"c ap traffic">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)), + + CAPTrafficSecret = + tls_v1:client_application_traffic_secret_0(HKDFAlgo, {master_secret, MasterSecret}, CHSF), + + %% {server} derive secret "tls13 s ap traffic": + %% + %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 + %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + %% + %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a + %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + %% + %% info (54 octets): 00 20 12 74 6c 73 31 33 20 73 20 61 70 20 74 72 + %% 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b + %% 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + %% + %% expanded (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 + %% 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43 + + %% PRK = MasterSecret + %% hash = CAPTHash + SAPTInfo = + hexstr2bin(" 00 20 12 74 6c 73 31 33 20 73 20 61 70 20 74 72 + 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b + 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"), + + SAPTrafficSecret = + hexstr2bin("a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 + 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43"), + + SAPTInfo = + tls_v1:create_info(<<"s ap traffic">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)), + + SAPTrafficSecret = + tls_v1:server_application_traffic_secret_0(HKDFAlgo, {master_secret, MasterSecret}, CHSF), + + %% {server} derive secret "tls13 exp master": + %% + %% PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 + %% 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + %% + %% hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a + %% 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + %% + %% info (52 octets): 00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73 + %% 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00 + %% 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + %% + %% expanded (32 octets): fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 + %% 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50 + + %% PRK = MasterSecret + %% hash = CAPTHash + ExporterInfo = + hexstr2bin("00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73 + 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00 + 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"), + + ExporterMasterSecret = + hexstr2bin("fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 + 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50"), + + ExporterInfo = + tls_v1:create_info(<<"exp master">>, CAPTHash, ssl_cipher:hash_size(HKDFAlgo)), + + ExporterMasterSecret = + tls_v1:exporter_master_secret(HKDFAlgo, {master_secret, MasterSecret}, CHSF), + + %% {server} derive write traffic keys for application data: + %% + %% PRK (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 50 32 + %% 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43 + %% + %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + %% + %% key expanded (16 octets): 9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac + %% 92 e3 56 + %% + %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + %% + %% iv expanded (12 octets): cf 78 2b 88 dd 83 54 9a ad f1 e9 84 + + %% PRK = SAPTrafficsecret + %% key info = WriteKeyInfo + %% iv info = WrtieIVInfo + SWKey = + hexstr2bin("9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac 92 e3 56"), + + SWIV = + hexstr2bin("cf 78 2b 88 dd 83 54 9a ad f1 e9 84"), + + {SWKey, SWIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, SAPTrafficSecret), + + %% {server} derive read traffic keys for handshake data: + %% + %% PRK (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e 2d 8f + %% 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21 + %% + %% key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + %% + %% key expanded (16 octets): db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 + %% 25 8d 01 + %% + %% iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + %% + %% iv expanded (12 octets): 5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f + + %% PRK = CHSTrafficsecret + %% key info = WriteKeyInfo + %% iv info = WrtieIVInfo + SRKey = + hexstr2bin("db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 25 8d 01"), + + SRIV = + hexstr2bin("5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f"), + + {SRKey, SRIV} = tls_v1:calculate_traffic_keys(HKDFAlgo, Cipher, CHSTrafficSecret). + +%%-------------------------------------------------------------------- +finished_verify_data() -> + [{doc,"Test TLS 1.3 Finished message handling"}]. + +finished_verify_data(_Config) -> + ClientHello = + hexstr2bin("01 00 00 c6 03 03 00 01 02 03 04 05 06 07 08 09 + 0a 0b 0c 0d 0e 0f 10 11 12 13 14 15 16 17 18 19 + 1a 1b 1c 1d 1e 1f 20 e0 e1 e2 e3 e4 e5 e6 e7 e8 + e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 + f9 fa fb fc fd fe ff 00 06 13 01 13 02 13 03 01 + 00 00 77 00 00 00 18 00 16 00 00 13 65 78 61 6d + 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e 65 74 00 + 0a 00 08 00 06 00 1d 00 17 00 18 00 0d 00 14 00 + 12 04 03 08 04 04 01 05 03 08 05 05 01 08 06 06 + 01 02 01 00 33 00 26 00 24 00 1d 00 20 35 80 72 + d6 36 58 80 d1 ae ea 32 9a df 91 21 38 38 51 ed + 21 a2 8e 3b 75 e9 65 d0 d2 cd 16 62 54 00 2d 00 + 02 01 01 00 2b 00 03 02 03 04"), + + ServerHello = + hexstr2bin("02 00 00 76 03 03 70 71 72 73 74 75 76 77 78 79 + 7a 7b 7c 7d 7e 7f 80 81 82 83 84 85 86 87 88 89 + 8a 8b 8c 8d 8e 8f 20 e0 e1 e2 e3 e4 e5 e6 e7 e8 + e9 ea eb ec ed ee ef f0 f1 f2 f3 f4 f5 f6 f7 f8 + f9 fa fb fc fd fe ff 13 01 00 00 2e 00 33 00 24 + 00 1d 00 20 9f d7 ad 6d cf f4 29 8d d3 f9 6d 5b + 1b 2a f9 10 a0 53 5b 14 88 d7 f8 fa bb 34 9a 98 + 28 80 b6 15 00 2b 00 02 03 04"), + + EncryptedExtensions = + hexstr2bin("08 00 00 02 00 00"), + + Certificate = + hexstr2bin("0b 00 03 2e 00 00 03 2a 00 03 25 30 82 03 21 30 + 82 02 09 a0 03 02 01 02 02 08 15 5a 92 ad c2 04 + 8f 90 30 0d 06 09 2a 86 48 86 f7 0d 01 01 0b 05 + 00 30 22 31 0b 30 09 06 03 55 04 06 13 02 55 53 + 31 13 30 11 06 03 55 04 0a 13 0a 45 78 61 6d 70 + 6c 65 20 43 41 30 1e 17 0d 31 38 31 30 30 35 30 + 31 33 38 31 37 5a 17 0d 31 39 31 30 30 35 30 31 + 33 38 31 37 5a 30 2b 31 0b 30 09 06 03 55 04 06 + 13 02 55 53 31 1c 30 1a 06 03 55 04 03 13 13 65 + 78 61 6d 70 6c 65 2e 75 6c 66 68 65 69 6d 2e 6e + 65 74 30 82 01 22 30 0d 06 09 2a 86 48 86 f7 0d + 01 01 01 05 00 03 82 01 0f 00 30 82 01 0a 02 82 + 01 01 00 c4 80 36 06 ba e7 47 6b 08 94 04 ec a7 + b6 91 04 3f f7 92 bc 19 ee fb 7d 74 d7 a8 0d 00 + 1e 7b 4b 3a 4a e6 0f e8 c0 71 fc 73 e7 02 4c 0d + bc f4 bd d1 1d 39 6b ba 70 46 4a 13 e9 4a f8 3d + f3 e1 09 59 54 7b c9 55 fb 41 2d a3 76 52 11 e1 + f3 dc 77 6c aa 53 37 6e ca 3a ec be c3 aa b7 3b + 31 d5 6c b6 52 9c 80 98 bc c9 e0 28 18 e2 0b f7 + f8 a0 3a fd 17 04 50 9e ce 79 bd 9f 39 f1 ea 69 + ec 47 97 2e 83 0f b5 ca 95 de 95 a1 e6 04 22 d5 + ee be 52 79 54 a1 e7 bf 8a 86 f6 46 6d 0d 9f 16 + 95 1a 4c f7 a0 46 92 59 5c 13 52 f2 54 9e 5a fb + 4e bf d7 7a 37 95 01 44 e4 c0 26 87 4c 65 3e 40 + 7d 7d 23 07 44 01 f4 84 ff d0 8f 7a 1f a0 52 10 + d1 f4 f0 d5 ce 79 70 29 32 e2 ca be 70 1f df ad + 6b 4b b7 11 01 f4 4b ad 66 6a 11 13 0f e2 ee 82 + 9e 4d 02 9d c9 1c dd 67 16 db b9 06 18 86 ed c1 + ba 94 21 02 03 01 00 01 a3 52 30 50 30 0e 06 03 + 55 1d 0f 01 01 ff 04 04 03 02 05 a0 30 1d 06 03 + 55 1d 25 04 16 30 14 06 08 2b 06 01 05 05 07 03 + 02 06 08 2b 06 01 05 05 07 03 01 30 1f 06 03 55 + 1d 23 04 18 30 16 80 14 89 4f de 5b cc 69 e2 52 + cf 3e a3 00 df b1 97 b8 1d e1 c1 46 30 0d 06 09 + 2a 86 48 86 f7 0d 01 01 0b 05 00 03 82 01 01 00 + 59 16 45 a6 9a 2e 37 79 e4 f6 dd 27 1a ba 1c 0b + fd 6c d7 55 99 b5 e7 c3 6e 53 3e ff 36 59 08 43 + 24 c9 e7 a5 04 07 9d 39 e0 d4 29 87 ff e3 eb dd + 09 c1 cf 1d 91 44 55 87 0b 57 1d d1 9b df 1d 24 + f8 bb 9a 11 fe 80 fd 59 2b a0 39 8c de 11 e2 65 + 1e 61 8c e5 98 fa 96 e5 37 2e ef 3d 24 8a fd e1 + 74 63 eb bf ab b8 e4 d1 ab 50 2a 54 ec 00 64 e9 + 2f 78 19 66 0d 3f 27 cf 20 9e 66 7f ce 5a e2 e4 + ac 99 c7 c9 38 18 f8 b2 51 07 22 df ed 97 f3 2e + 3e 93 49 d4 c6 6c 9e a6 39 6d 74 44 62 a0 6b 42 + c6 d5 ba 68 8e ac 3a 01 7b dd fc 8e 2c fc ad 27 + cb 69 d3 cc dc a2 80 41 44 65 d3 ae 34 8c e0 f3 + 4a b2 fb 9c 61 83 71 31 2b 19 10 41 64 1c 23 7f + 11 a5 d6 5c 84 4f 04 04 84 99 38 71 2b 95 9e d6 + 85 bc 5c 5d d6 45 ed 19 90 94 73 40 29 26 dc b4 + 0e 34 69 a1 59 41 e8 e2 cc a8 4b b6 08 46 36 a0 + 00 00"), + + CertificateVerify = + hexstr2bin("0f 00 01 04 08 04 01 00 17 fe b5 33 ca 6d 00 7d + 00 58 25 79 68 42 4b bc 3a a6 90 9e 9d 49 55 75 + 76 a5 20 e0 4a 5e f0 5f 0e 86 d2 4f f4 3f 8e b8 + 61 ee f5 95 22 8d 70 32 aa 36 0f 71 4e 66 74 13 + 92 6e f4 f8 b5 80 3b 69 e3 55 19 e3 b2 3f 43 73 + df ac 67 87 06 6d cb 47 56 b5 45 60 e0 88 6e 9b + 96 2c 4a d2 8d ab 26 ba d1 ab c2 59 16 b0 9a f2 + 86 53 7f 68 4f 80 8a ef ee 73 04 6c b7 df 0a 84 + fb b5 96 7a ca 13 1f 4b 1c f3 89 79 94 03 a3 0c + 02 d2 9c bd ad b7 25 12 db 9c ec 2e 5e 1d 00 e5 + 0c af cf 6f 21 09 1e bc 4f 25 3c 5e ab 01 a6 79 + ba ea be ed b9 c9 61 8f 66 00 6b 82 44 d6 62 2a + aa 56 88 7c cf c6 6a 0f 38 51 df a1 3a 78 cf f7 + 99 1e 03 cb 2c 3a 0e d8 7d 73 67 36 2e b7 80 5b + 00 b2 52 4f f2 98 a4 da 48 7c ac de af 8a 23 36 + c5 63 1b 3e fa 93 5b b4 11 e7 53 ca 13 b0 15 fe + c7 e4 a7 30 f1 36 9f 9e"), + + BaseKey = + hexstr2bin("a2 06 72 65 e7 f0 65 2a 92 3d 5d 72 ab 04 67 c4 + 61 32 ee b9 68 b6 a3 2d 31 1c 80 58 68 54 88 14"), + + VerifyData = + hexstr2bin("ea 6e e1 76 dc cc 4a f1 85 9e 9e 4e 93 f7 97 ea + c9 a7 8c e4 39 30 1e 35 27 5a d4 3f 3c dd bd e3"), + + Messages = [CertificateVerify, + Certificate, + EncryptedExtensions, + ServerHello, + ClientHello], + + FinishedKey = tls_v1:finished_key(BaseKey, sha256), + VerifyData = tls_v1:finished_verify_data(FinishedKey, sha256, Messages). + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- + +hexstr2int(S) -> + B = hexstr2bin(S), + Bits = size(B) * 8, + <<Integer:Bits/integer>> = B, + Integer. + +hexstr2bin(S) when is_binary(S) -> + hexstr2bin(S, <<>>); +hexstr2bin(S) -> + hexstr2bin(list_to_binary(S), <<>>). +%% +hexstr2bin(<<>>, Acc) -> + Acc; +hexstr2bin(<<C,T/binary>>, Acc) when C =:= 32; %% SPACE + C =:= 10; %% LF + C =:= 13 -> %% CR + hexstr2bin(T, Acc); +hexstr2bin(<<X,Y,T/binary>>, Acc) -> + I = hex2int(X) * 16 + hex2int(Y), + hexstr2bin(T, <<Acc/binary,I>>). + +hex2int(C) when $0 =< C, C =< $9 -> + C - $0; +hex2int(C) when $A =< C, C =< $F -> + C - $A + 10; +hex2int(C) when $a =< C, C =< $f -> + C - $a + 10. diff --git a/lib/ssl/test/tls_1_3_version_SUITE.erl b/lib/ssl/test/tls_1_3_version_SUITE.erl new file mode 100644 index 0000000000..f0b224d4e5 --- /dev/null +++ b/lib/ssl/test/tls_1_3_version_SUITE.erl @@ -0,0 +1,153 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(tls_1_3_version_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct.hrl"). +-include_lib("public_key/include/public_key.hrl"). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- +all() -> + [ + {group, 'tlsv1.3'} + ]. + +groups() -> + [ + {'tlsv1.3', [], cert_groups()}, + {rsa, [], tests()}, + {ecdsa, [], tests()} + ]. + +cert_groups() -> + [{group, rsa}, + {group, ecdsa}]. + +tests() -> + [tls13_client_tls12_server, + tls13_client_with_ext_tls12_server, + tls12_client_tls13_server]. + +init_per_suite(Config) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + [{client_type, erlang}, {server_type, erlang} | + Config] + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:stop(crypto). + +init_per_group(rsa, Config0) -> + Config = ssl_test_lib:make_rsa_cert(Config0), + COpts = proplists:get_value(client_rsa_opts, Config), + SOpts = proplists:get_value(server_rsa_opts, Config), + [{client_cert_opts, COpts}, {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, lists:delete(client_cert_opts, Config))]; +init_per_group(ecdsa, Config0) -> + PKAlg = crypto:supports(public_keys), + case lists:member(ecdsa, PKAlg) andalso + (lists:member(ecdh, PKAlg) orelse lists:member(dh, PKAlg)) of + true -> + Config = ssl_test_lib:make_ecdsa_cert(Config0), + COpts = proplists:get_value(client_ecdsa_opts, Config), + SOpts = proplists:get_value(server_ecdsa_opts, Config), + [{client_cert_opts, COpts}, {server_cert_opts, SOpts} | + lists:delete(server_cert_opts, lists:delete(client_cert_opts, Config))]; + false -> + {skip, "Missing EC crypto support"} + end; +init_per_group(GroupName, Config) -> + ssl_test_lib:clean_tls_version(Config), + case ssl_test_lib:is_tls_version(GroupName) andalso + ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName, Config); + _ -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl:start(), + Config; + false -> + {skip, "Missing crypto support"} + end + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- + +tls13_client_tls12_server() -> + [{doc,"Test that a TLS 1.3 client can connect to a TLS 1.2 server."}]. + +tls13_client_tls12_server(Config) when is_list(Config) -> + ClientOpts = [{versions, + ['tlsv1.3', 'tlsv1.2']} | ssl_test_lib:ssl_options(client_cert_opts, Config)], + ServerOpts = [{versions, + ['tlsv1.1', 'tlsv1.2']} | ssl_test_lib:ssl_options(server_cert_opts, Config)], + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +tls13_client_with_ext_tls12_server() -> + [{doc,"Test basic connection between TLS 1.2 server and TLS 1.3 client when " + "client has TLS 1.3 specsific extensions"}]. + +tls13_client_with_ext_tls12_server(Config) -> + ClientOpts0 = ssl_test_lib:ssl_options(client_cert_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_cert_opts, Config), + + ServerOpts = [{versions, ['tlsv1.2']}|ServerOpts0], + ClientOpts = [{versions, ['tlsv1.2','tlsv1.3']}, + {signature_algs_cert, [ecdsa_secp384r1_sha384, + ecdsa_secp256r1_sha256, + rsa_pss_rsae_sha256, + rsa_pkcs1_sha256, + {sha256,rsa},{sha256,dsa}]}|ClientOpts0], + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + +tls12_client_tls13_server() -> + [{doc,"Test that a TLS 1.2 client can connect to a TLS 1.3 server."}]. + +tls12_client_tls13_server(Config) when is_list(Config) -> + ClientOpts = [{versions, + ['tlsv1.1', 'tlsv1.2']} | ssl_test_lib:ssl_options(client_cert_opts, Config)], + ServerOpts = [{versions, + ['tlsv1.3', 'tlsv1.2']} | ssl_test_lib:ssl_options(server_cert_opts, Config)], + ssl_test_lib:basic_test(ClientOpts, ServerOpts, Config). + diff --git a/lib/ssl/test/tls_api_SUITE.erl b/lib/ssl/test/tls_api_SUITE.erl new file mode 100644 index 0000000000..5a74ec1892 --- /dev/null +++ b/lib/ssl/test/tls_api_SUITE.erl @@ -0,0 +1,880 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2019-2019. 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(tls_api_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). +-include_lib("common_test/include/ct.hrl"). +-include_lib("ssl/src/ssl_record.hrl"). +-include_lib("ssl/src/ssl_internal.hrl"). +-include_lib("ssl/src/ssl_api.hrl"). +-include_lib("ssl/src/tls_handshake.hrl"). + +-define(SLEEP, 500). + +%%-------------------------------------------------------------------- +%% Common Test interface functions ----------------------------------- +%%-------------------------------------------------------------------- + +all() -> + [ + {group, 'tlsv1.3'}, + {group, 'tlsv1.2'}, + {group, 'tlsv1.1'}, + {group, 'tlsv1'}, + {group, 'sslv3'} + ]. + +groups() -> + [ + {'tlsv1.3', [], api_tests() -- [sockname]}, + {'tlsv1.2', [], api_tests()}, + {'tlsv1.1', [], api_tests()}, + {'tlsv1', [], api_tests()}, + {'sslv3', [], api_tests() ++ [ssl3_cipher_suite_limitation]} + ]. + +api_tests() -> + [ + tls_upgrade, + tls_upgrade_with_timeout, + tls_downgrade, + tls_shutdown, + tls_shutdown_write, + tls_shutdown_both, + tls_shutdown_error, + tls_client_closes_socket, + tls_closed_in_active_once, + tls_tcp_msg, + tls_tcp_msg_big, + tls_dont_crash_on_handshake_garbage, + tls_tcp_error_propagation_in_active_mode, + peername, + sockname, + tls_server_handshake_timeout, + transport_close, + emulated_options, + accept_pool, + reuseaddr + ]. + +init_per_suite(Config0) -> + catch crypto:stop(), + try crypto:start() of + ok -> + ssl_test_lib:clean_start(), + ssl_test_lib:make_rsa_cert(Config0) + catch _:_ -> + {skip, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + ssl:stop(), + application:unload(ssl), + application:stop(crypto). + + +init_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + case ssl_test_lib:sufficient_crypto_support(GroupName) of + true -> + ssl_test_lib:init_tls_version(GroupName, Config); + false -> + {skip, "Missing crypto support"} + end; + _ -> + ssl:start(), + Config + end. + +end_per_group(GroupName, Config) -> + case ssl_test_lib:is_tls_version(GroupName) of + true -> + ssl_test_lib:clean_tls_version(Config); + false -> + Config + end. + +init_per_testcase(_TestCase, Config) -> + ssl_test_lib:ct_log_supported_protocol_versions(Config), + ct:timetrap({seconds, 10}), + Config. + +end_per_testcase(_TestCase, Config) -> + Config. + +%%-------------------------------------------------------------------- +%% Test Cases -------------------------------------------------------- +%%-------------------------------------------------------------------- +tls_upgrade() -> + [{doc,"Test that you can upgrade an tcp connection to an ssl connection"}]. + +tls_upgrade(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + TcpOpts = [binary, {reuseaddr, true}], + + Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, + upgrade_result, []}}, + {tcp_options, + [{active, false} | TcpOpts]}, + {ssl_options, [{verify, verify_peer} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_upgrade_client([{node, ClientNode}, + {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, upgrade_result, []}}, + {tcp_options, [binary]}, + {ssl_options, [{verify, verify_peer}, + {server_name_indication, Hostname} | ClientOpts]}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +tls_upgrade_with_timeout() -> + [{doc,"Test ssl_accept/3"}]. + +tls_upgrade_with_timeout(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + TcpOpts = [binary, {reuseaddr, true}], + + Server = ssl_test_lib:start_upgrade_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {timeout, 5000}, + {mfa, {?MODULE, + upgrade_result, []}}, + {tcp_options, + [{active, false} | TcpOpts]}, + {ssl_options, [{verify, verify_peer} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_upgrade_client([{node, ClientNode}, + {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, upgrade_result, []}}, + {tcp_options, TcpOpts}, + {ssl_options, [{verify, verify_peer}, + {server_name_indication, Hostname} | ClientOpts]}]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +tls_downgrade() -> + [{doc,"Test that you can downgarde an ssl connection to an tcp connection"}]. +tls_downgrade(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, tls_downgrade_result, [self()]}}, + {options, [{active, false}, {verify, verify_peer} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, tls_downgrade_result, [self()]}}, + {options, [{active, false}, {verify, verify_peer} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ready, Client, ready), + + Server ! go, + Client ! go, + + ssl_test_lib:check_result(Server, ok, Client, ok), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + + +%%-------------------------------------------------------------------- +tls_shutdown() -> + [{doc,"Test API function ssl:shutdown/2"}]. +tls_shutdown(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, tls_shutdown_result, [server]}}, + {options, [{exit_on_close, false}, + {active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, + {?MODULE, tls_shutdown_result, [client]}}, + {options, + [{exit_on_close, false}, + {active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +tls_shutdown_write() -> + [{doc,"Test API function ssl:shutdown/2 with option write."}]. +tls_shutdown_write(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, tls_shutdown_write_result, [server]}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, tls_shutdown_write_result, [client]}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, {error, closed}). + +%%-------------------------------------------------------------------- +tls_shutdown_both() -> + [{doc,"Test API function ssl:shutdown/2 with option both."}]. +tls_shutdown_both(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, tls_shutdown_both_result, [server]}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, tls_shutdown_both_result, [client]}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server, ok, Client, {error, closed}). + +%%-------------------------------------------------------------------- +tls_shutdown_error() -> + [{doc,"Test ssl:shutdown/2 error handling"}]. +tls_shutdown_error(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + Port = ssl_test_lib:inet_port(node()), + {ok, Listen} = ssl:listen(Port, ServerOpts), + {error, enotconn} = ssl:shutdown(Listen, read_write), + ok = ssl:close(Listen), + {error, closed} = ssl:shutdown(Listen, read_write). +%%-------------------------------------------------------------------- +tls_client_closes_socket() -> + [{doc,"Test what happens when client closes socket before handshake is compleated"}]. + +tls_client_closes_socket(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + TcpOpts = [binary, {reuseaddr, true}], + + Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {tcp_options, TcpOpts}, + {ssl_options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Connect = fun() -> + {ok, _Socket} = rpc:call(ClientNode, gen_tcp, connect, + [Hostname, Port, [binary]]), + %% Make sure that ssl_accept is called before + %% client process ends and closes socket. + ct:sleep(?SLEEP) + end, + + _Client = spawn_link(Connect), + + ssl_test_lib:check_result(Server, {error,closed}). + +%%-------------------------------------------------------------------- +tls_closed_in_active_once() -> + [{doc, "Test that ssl_closed is delivered in active once with non-empty buffer, check ERL-420."}]. + +tls_closed_in_active_once(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {_ClientNode, _ServerNode, Hostname} = ssl_test_lib:run_where(Config), + TcpOpts = [binary, {reuseaddr, true}], + Port = ssl_test_lib:inet_port(node()), + Server = fun() -> + {ok, Listen} = gen_tcp:listen(Port, TcpOpts), + {ok, TcpServerSocket} = gen_tcp:accept(Listen), + {ok, ServerSocket} = ssl:handshake(TcpServerSocket, ServerOpts), + lists:foreach( + fun(_) -> + ssl:send(ServerSocket, "some random message\r\n") + end, lists:seq(1, 20)), + %% Close TCP instead of SSL socket to trigger the bug: + gen_tcp:close(TcpServerSocket), + gen_tcp:close(Listen) + end, + spawn_link(Server), + {ok, Socket} = ssl:connect(Hostname, Port, [{active, false} | ClientOpts]), + Result = tls_closed_in_active_once_loop(Socket), + ssl:close(Socket), + case Result of + ok -> ok; + _ -> ct:fail(Result) + end. +%%-------------------------------------------------------------------- +tls_tcp_msg() -> + [{doc,"Test what happens when a tcp tries to connect, i,e. a bad (ssl) packet is sent first"}]. + +tls_tcp_msg(Config) when is_list(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + TcpOpts = [binary, {reuseaddr, true}, {active, false}], + + Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {timeout, 5000}, + {mfa, {?MODULE, dummy, []}}, + {tcp_options, TcpOpts}, + {ssl_options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), + ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]), + gen_tcp:send(Socket, "<SOME GARBLED NON SSL MESSAGE>"), + + receive + {tcp_closed, Socket} -> + receive + {Server, {error, Error}} -> + ct:log("Error ~p", [Error]) + end + end. +%%-------------------------------------------------------------------- +tls_tcp_msg_big() -> + [{doc,"Test what happens when a tcp tries to connect, i,e. a bad big (ssl) packet is sent first"}]. + +tls_tcp_msg_big(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + TcpOpts = [binary, {reuseaddr, true}], + + Rand = crypto:strong_rand_bytes(?MAX_CIPHER_TEXT_LENGTH+1), + Server = ssl_test_lib:start_upgrade_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {timeout, 5000}, + {mfa, {?MODULE, dummy, []}}, + {tcp_options, TcpOpts}, + {ssl_options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {packet, 0}]), + ct:log("Testcase ~p connected to Server ~p ~n", [self(), Server]), + + gen_tcp:send(Socket, <<?BYTE(0), + ?BYTE(3), ?BYTE(1), ?UINT16(?MAX_CIPHER_TEXT_LENGTH), Rand/binary>>), + + receive + {tcp_closed, Socket} -> + receive + {Server, {error, timeout}} -> + ct:fail("hangs"); + {Server, {error, Error}} -> + ct:log("Error ~p", [Error]); + {'EXIT', Server, _} -> + ok + end + end. + +%%-------------------------------------------------------------------- +tls_dont_crash_on_handshake_garbage() -> + [{doc, "Ensure SSL server worker thows an alert on garbage during handshake " + "instead of crashing and exposing state to user code"}]. + +tls_dont_crash_on_handshake_garbage(Config) -> + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + unlink(Server), monitor(process, Server), + Port = ssl_test_lib:inet_port(Server), + + {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]), + + % Send hello and garbage record + ok = gen_tcp:send(Socket, + [<<22, 3,3, 49:16, 1, 45:24, 3,3, % client_hello + 16#deadbeef:256, % 32 'random' bytes = 256 bits + 0, 6:16, 0,255, 0,61, 0,57, 1, 0 >>, % some hello values + + <<22, 3,3, 5:16, 92,64,37,228,209>> % garbage + ]), + % Send unexpected change_cipher_spec + ok = gen_tcp:send(Socket, <<20, 3,3, 12:16, 111,40,244,7,137,224,16,109,197,110,249,152>>), + + % Ensure we receive an alert, not sudden disconnect + {ok, <<21, _/binary>>} = drop_handshakes(Socket, 1000). + +%%-------------------------------------------------------------------- +tls_tcp_error_propagation_in_active_mode() -> + [{doc,"Test that process recives {ssl_error, Socket, closed} when tcp error ocurres"}]. +tls_tcp_error_propagation_in_active_mode(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + {Client, #sslsocket{pid=[Pid|_]} = SslSocket} = ssl_test_lib:start_client([return_socket, + {node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, receive_msg, []}}, + {options, ClientOpts}]), + + {status, _, _, StatusInfo} = sys:get_status(Pid), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + StaticEnv = element(2, State), + Socket = element(11, StaticEnv), + %% Fake tcp error + Pid ! {tcp_error, Socket, etimedout}, + + ssl_test_lib:check_result(Client, {ssl_closed, SslSocket}). + +%%-------------------------------------------------------------------- +peername() -> + [{doc,"Test API function peername/1"}]. + +peername(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, peername_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, peername_result, []}}, + {options, [{port, 0} | ClientOpts]}]), + + ClientPort = ssl_test_lib:inet_port(Client), + ServerIp = ssl_test_lib:node_to_hostip(ServerNode, server), + ClientIp = ssl_test_lib:node_to_hostip(ClientNode, client), + ServerMsg = {ok, {ClientIp, ClientPort}}, + ClientMsg = {ok, {ServerIp, Port}}, + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). + +%%-------------------------------------------------------------------- +sockname() -> + [{doc,"Test API function sockname/1"}]. +sockname(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, sockname_result, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, sockname_result, []}}, + {options, [{port, 0} | ClientOpts]}]), + + ClientPort = ssl_test_lib:inet_port(Client), + ServerIp = ssl_test_lib:node_to_hostip(ServerNode, server), + ClientIp = ssl_test_lib:node_to_hostip(ClientNode, client), + ServerMsg = {ok, {ServerIp, Port}}, + ClientMsg = {ok, {ClientIp, ClientPort}}, + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), Client, Server]), + + ssl_test_lib:check_result(Server, ServerMsg, Client, ClientMsg), + + ssl_test_lib:close(Server), + ssl_test_lib:close(Client). +%%-------------------------------------------------------------------- +tls_server_handshake_timeout() -> + [{doc,"Test server handshake timeout"}]. + +tls_server_handshake_timeout(Config) -> + process_flag(trap_exit, true), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {timeout, 5000}, + {mfa, {ssl_test_lib, + no_result_msg, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + {ok, CSocket} = gen_tcp:connect(Hostname, Port, [binary, {active, true}]), + + receive + {tcp_closed, CSocket} -> + ssl_test_lib:check_result(Server, {error, timeout}), + receive + {'EXIT', Server, _} -> + %% Make sure supervisor had time to react on process exit + %% Could we come up with a better solution to this? + ct:sleep(500), + [] = supervisor:which_children(tls_connection_sup) + end + end. +transport_close() -> + [{doc, "Test what happens if socket is closed on TCP level after a while of normal operation"}]. +transport_close(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false} | ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + {ok, TcpS} = rpc:call(ClientNode, gen_tcp, connect, + [Hostname,Port,[binary, {active, false}]]), + {ok, SslS} = rpc:call(ClientNode, ssl, connect, + [TcpS,[{active, false}|ClientOpts]]), + + ct:log("Testcase ~p, Client ~p Server ~p ~n", + [self(), self(), Server]), + ok = ssl:send(SslS, "Hello world"), + {ok,<<"Hello world">>} = ssl:recv(SslS, 11), + gen_tcp:close(TcpS), + {error, _} = ssl:send(SslS, "Hello world"). + +%%-------------------------------------------------------------------- +ssl3_cipher_suite_limitation() -> + [{doc,"Test a SSLv3 client cannot negotiate a TLSv* cipher suite."}]. +ssl3_cipher_suite_limitation(Config) when is_list(Config) -> + + {_ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + Server = ssl_test_lib:start_server_error([{node, ServerNode}, {port, 0}, + {from, self()}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + {ok, Socket} = gen_tcp:connect(Hostname, Port, [binary, {active, false}]), + ok = gen_tcp:send(Socket, + <<22, 3,0, 49:16, % handshake, SSL 3.0, length + 1, 45:24, % client_hello, length + 3,0, % SSL 3.0 + 16#deadbeef:256, % 32 'random' bytes = 256 bits + 0, % no session ID + %% three cipher suites -- null, one with sha256 hash and one with sha hash + 6:16, 0,255, 0,61, 0,57, + 1, 0 % no compression + >>), + {ok, <<22, RecMajor:8, RecMinor:8, _RecLen:16, 2, HelloLen:24>>} = gen_tcp:recv(Socket, 9, 10000), + {ok, <<HelloBin:HelloLen/binary>>} = gen_tcp:recv(Socket, HelloLen, 5000), + ServerHello = tls_handshake:decode_handshake({RecMajor, RecMinor}, 2, HelloBin), + case ServerHello of + #server_hello{server_version = {3,0}, cipher_suite = <<0,57>>} -> + ok; + _ -> + ct:fail({unexpected_server_hello, ServerHello}) + end. +%%-------------------------------------------------------------------- +emulated_options() -> + [{doc,"Test API function getopts/2 and setopts/2"}]. + +emulated_options(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Values = [{mode, list}, {packet, 0}, {header, 0}, + {active, true}], + %% Shall be the reverse order of Values! + Options = [active, header, packet, mode], + + NewValues = [{mode, binary}, {active, once}], + %% Shall be the reverse order of NewValues! + NewOptions = [active, mode], + + Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, tls_socket_options_result, + [Options, Values, NewOptions, NewValues]}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + Client = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {?MODULE, tls_socket_options_result, + [Options, Values, NewOptions, NewValues]}}, + {options, ClientOpts}]), + + ssl_test_lib:check_result(Server, ok, Client, ok), + + ssl_test_lib:close(Server), + + {ok, Listen} = ssl:listen(0, ServerOpts), + {ok,[{mode,list}]} = ssl:getopts(Listen, [mode]), + ok = ssl:setopts(Listen, [{mode, binary}]), + {ok,[{mode, binary}]} = ssl:getopts(Listen, [mode]), + {ok,[{recbuf, _}]} = ssl:getopts(Listen, [recbuf]), + ssl:close(Listen). +accept_pool() -> + [{doc,"Test having an accept pool."}]. +accept_pool(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server0 = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {accepters, 3}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server0), + [Server1, Server2] = ssl_test_lib:accepters(2), + + Client0 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts} + ]), + + Client1 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts} + ]), + + Client2 = ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result_active, []}}, + {options, ClientOpts} + ]), + + ssl_test_lib:check_ok([Server0, Server1, Server2, Client0, Client1, Client2]), + + ssl_test_lib:close(Server0), + ssl_test_lib:close(Server1), + ssl_test_lib:close(Server2), + ssl_test_lib:close(Client0), + ssl_test_lib:close(Client1), + ssl_test_lib:close(Client2). + +%%-------------------------------------------------------------------- +reuseaddr() -> + [{doc,"Test reuseaddr option"}]. + +reuseaddr(Config) when is_list(Config) -> + ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), + {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), + Server = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{active, false}, {reuseaddr, true}| ServerOpts]}]), + Port = ssl_test_lib:inet_port(Server), + Client = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {options, [{active, false} | ClientOpts]}]), + ssl_test_lib:close(Server), + ssl_test_lib:close(Client), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, Port}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false}, {reuseaddr, true} | ServerOpts]}]), + Client1 = + ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, + {host, Hostname}, + {from, self()}, + {mfa, {ssl_test_lib, send_recv_result, []}}, + {options, [{active, false} | ClientOpts]}]), + + ssl_test_lib:check_result(Server1, ok, Client1, ok), + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client1). + +%%-------------------------------------------------------------------- +%% Internal functions ------------------------------------------------ +%%-------------------------------------------------------------------- + +upgrade_result(Socket) -> + ssl:setopts(Socket, [{active, true}]), + ok = ssl:send(Socket, "Hello world"), + %% Make sure binary is inherited from tcp socket and that we do + %% not get the list default! + receive + {ssl, _, <<"H">>} -> + receive + {ssl, _, <<"ello world">>} -> + ok + end; + {ssl, _, <<"Hello world">>} -> + ok + end. +tls_downgrade_result(Socket, Pid) -> + ok = ssl_test_lib:send_recv_result(Socket), + Pid ! {self(), ready}, + receive + go -> + ok + end, + case ssl:close(Socket, {self(), 10000}) of + {ok, TCPSocket} -> + inet:setopts(TCPSocket, [{active, true}]), + gen_tcp:send(TCPSocket, "Downgraded"), + receive + {tcp, TCPSocket, <<"Downgraded">>} -> + ok; + {tcp_closed, TCPSocket} -> + ct:fail("Peer timed out, downgrade aborted"), + ok; + Other -> + {error, Other} + end; + {error, timeout} -> + ct:fail("Timed out, downgrade aborted"), + ok; + Fail -> + {error, Fail} + end. + +tls_shutdown_result(Socket, server) -> + ssl:send(Socket, "Hej"), + ok = ssl:shutdown(Socket, write), + {ok, "Hej hopp"} = ssl:recv(Socket, 8), + ok; + +tls_shutdown_result(Socket, client) -> + ssl:send(Socket, "Hej hopp"), + ok = ssl:shutdown(Socket, write), + {ok, "Hej"} = ssl:recv(Socket, 3), + ok. + +tls_shutdown_write_result(Socket, server) -> + ct:sleep(?SLEEP), + ssl:shutdown(Socket, write); +tls_shutdown_write_result(Socket, client) -> + ssl:recv(Socket, 0). + +tls_shutdown_both_result(Socket, server) -> + ct:sleep(?SLEEP), + ssl:shutdown(Socket, read_write); +tls_shutdown_both_result(Socket, client) -> + ssl:recv(Socket, 0). + +tls_closed_in_active_once_loop(Socket) -> + case ssl:setopts(Socket, [{active, once}]) of + ok -> + receive + {ssl, Socket, _} -> + tls_closed_in_active_once_loop(Socket); + {ssl_closed, Socket} -> + ok + after 5000 -> + no_ssl_closed_received + end; + {error, closed} -> + ok + end. + +drop_handshakes(Socket, Timeout) -> + {ok, <<RecType:8, _RecMajor:8, _RecMinor:8, RecLen:16>> = Header} = gen_tcp:recv(Socket, 5, Timeout), + {ok, <<Frag:RecLen/binary>>} = gen_tcp:recv(Socket, RecLen, Timeout), + case RecType of + 22 -> drop_handshakes(Socket, Timeout); + _ -> {ok, <<Header/binary, Frag/binary>>} + end. + +receive_msg(_) -> + receive + Msg -> + Msg + end. + +sockname_result(S) -> + ssl:sockname(S). + +peername_result(S) -> + ssl:peername(S). + +tls_socket_options_result(Socket, Options, DefaultValues, NewOptions, NewValues) -> + %% Test get/set emulated opts + {ok, DefaultValues} = ssl:getopts(Socket, Options), + ssl:setopts(Socket, NewValues), + {ok, NewValues} = ssl:getopts(Socket, NewOptions), + %% Test get/set inet opts + {ok,[{nodelay,false}]} = ssl:getopts(Socket, [nodelay]), + ssl:setopts(Socket, [{nodelay, true}]), + {ok,[{nodelay, true}]} = ssl:getopts(Socket, [nodelay]), + {ok, All} = ssl:getopts(Socket, []), + ct:log("All opts ~p~n", [All]), + ok. + diff --git a/lib/stdlib/doc/src/binary.xml b/lib/stdlib/doc/src/binary.xml index f3d4edd30f..fd991f258b 100644 --- a/lib/stdlib/doc/src/binary.xml +++ b/lib/stdlib/doc/src/binary.xml @@ -505,15 +505,16 @@ store(Binary, GBSet) -> <<1,1,1,1,1 ... 2> byte_size(A). 100 -3> binary:referenced_byte_size(A) +3> binary:referenced_byte_size(A). 100 -4> <<_:10/binary,B:10/binary,_/binary>> = A. +4> <<B:10/binary, C:90/binary>> = A. <<1,1,1,1,1 ... -5> byte_size(B). -10 -6> binary:referenced_byte_size(B) -100</code> - +5> {byte_size(B), binary:referenced_byte_size(B)}. +{10,10} +6> {byte_size(C), binary:referenced_byte_size(C)}. +{90,100}</code> + <p>In the above example, the small binary <c>B</c> was copied while the + larger binary <c>C</c> references binary <c>A</c>.</p> <note> <p>Binary data is shared among processes. If another process still references the larger binary, copying the part this diff --git a/lib/stdlib/doc/src/gen_statem.xml b/lib/stdlib/doc/src/gen_statem.xml index 6f6849a19d..ef548ad643 100644 --- a/lib/stdlib/doc/src/gen_statem.xml +++ b/lib/stdlib/doc/src/gen_statem.xml @@ -40,7 +40,7 @@ <p> This reference manual describes types generated from the types in the <c>gen_statem</c> source code, so they are correct. - However, the generated descriptions also reflect the type hiearchy, + However, the generated descriptions also reflect the type hierarchy, which makes them kind of hard to read. </p> <p> diff --git a/lib/stdlib/doc/src/notes.xml b/lib/stdlib/doc/src/notes.xml index 092056ffde..66624c43be 100644 --- a/lib/stdlib/doc/src/notes.xml +++ b/lib/stdlib/doc/src/notes.xml @@ -306,6 +306,40 @@ </section> +<section><title>STDLIB 3.8.2.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a bug that could cause a loop when formatting + terms using the control sequences <c>p</c> or <c>P</c> + and limiting the output with the option + <c>chars_limit</c>. </p> + <p> + Own Id: OTP-15875 Aux Id: ERL-967 </p> + </item> + </list> + </section> + +</section> + +<section><title>STDLIB 3.8.2.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> Fix a bug that could cause a failure when formatting + binaries using the control sequences <c>p</c> or <c>P</c> + and limiting the output with the option + <c>chars_limit</c>. </p> + <p> + Own Id: OTP-15847 Aux Id: ERL-957 </p> + </item> + </list> + </section> + +</section> + <section><title>STDLIB 3.8.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/stdlib/src/ordsets.erl b/lib/stdlib/src/ordsets.erl index 176047079b..95b83ded8c 100644 --- a/lib/stdlib/src/ordsets.erl +++ b/lib/stdlib/src/ordsets.erl @@ -150,13 +150,8 @@ union(Es1, []) -> Es1. OrdsetList :: [ordset(T)], Ordset :: ordset(T). -union([S1,S2|Ss]) -> - union1(union(S1, S2), Ss); -union([S]) -> S; -union([]) -> []. - -union1(S1, [S2|Ss]) -> union1(union(S1, S2), Ss); -union1(S1, []) -> S1. +union(OrdsetList) -> + lists:umerge(OrdsetList). %% intersection(OrdSet1, OrdSet2) -> OrdSet. %% Return the intersection of OrdSet1 and OrdSet2. diff --git a/lib/stdlib/src/re.erl b/lib/stdlib/src/re.erl index 726b409d4d..197564b895 100644 --- a/lib/stdlib/src/re.erl +++ b/lib/stdlib/src/re.erl @@ -33,6 +33,8 @@ %%% BIFs +-export([internal_run/4]). + -export([version/0, compile/1, compile/2, run/2, run/3, inspect/2]). -spec version() -> binary(). @@ -100,6 +102,40 @@ run(_, _) -> run(_, _, _) -> erlang:nif_error(undef). +-spec internal_run(Subject, RE, Options, FirstCall) -> {match, Captured} | + match | + nomatch | + {error, ErrType} when + Subject :: iodata() | unicode:charlist(), + RE :: mp() | iodata() | unicode:charlist(), + Options :: [Option], + Option :: anchored | global | notbol | noteol | notempty + | notempty_atstart | report_errors + | {offset, non_neg_integer()} | + {match_limit, non_neg_integer()} | + {match_limit_recursion, non_neg_integer()} | + {newline, NLSpec :: nl_spec()} | + bsr_anycrlf | bsr_unicode | {capture, ValueSpec} | + {capture, ValueSpec, Type} | CompileOpt, + Type :: index | list | binary, + ValueSpec :: all | all_but_first | all_names | first | none | ValueList, + ValueList :: [ValueID], + ValueID :: integer() | string() | atom(), + CompileOpt :: compile_option(), + Captured :: [CaptureData] | [[CaptureData]], + CaptureData :: {integer(), integer()} + | ListConversionData + | binary(), + ListConversionData :: string() + | {error, string(), binary()} + | {incomplete, string(), binary()}, + ErrType :: match_limit | match_limit_recursion | {compile, CompileErr}, + CompileErr :: {ErrString :: string(), Position :: non_neg_integer()}, + FirstCall :: boolean(). + +internal_run(_, _, _, _) -> + erlang:nif_error(undef). + -spec inspect(MP,Item) -> {namelist, [ binary() ]} when MP :: mp(), Item :: namelist. @@ -765,17 +801,17 @@ do_grun(FlatSubject,Subject,Unicode,CRLF,RE,{Options0,NeedClean}) -> try postprocess(loopexec(FlatSubject,RE,InitialOffset, byte_size(FlatSubject), - Unicode,CRLF,StrippedOptions), + Unicode,CRLF,StrippedOptions,true), SelectReturn,ConvertReturn,FlatSubject,Unicode) catch throw:ErrTuple -> ErrTuple end. -loopexec(_,_,X,Y,_,_,_) when X > Y -> +loopexec(_,_,X,Y,_,_,_,_) when X > Y -> {match,[]}; -loopexec(Subject,RE,X,Y,Unicode,CRLF,Options) -> - case re:run(Subject,RE,[{offset,X}]++Options) of +loopexec(Subject,RE,X,Y,Unicode,CRLF,Options, First) -> + case re:internal_run(Subject,RE,[{offset,X}]++Options,First) of {error, Err} -> throw({error,Err}); nomatch -> @@ -784,11 +820,11 @@ loopexec(Subject,RE,X,Y,Unicode,CRLF,Options) -> {match,Rest} = case B>0 of true -> - loopexec(Subject,RE,A+B,Y,Unicode,CRLF,Options); + loopexec(Subject,RE,A+B,Y,Unicode,CRLF,Options,false); false -> {match,M} = - case re:run(Subject,RE,[{offset,X},notempty_atstart, - anchored]++Options) of + case re:internal_run(Subject,RE,[{offset,X},notempty_atstart, + anchored]++Options,false) of nomatch -> {match,[]}; {match,Other} -> @@ -801,7 +837,7 @@ loopexec(Subject,RE,X,Y,Unicode,CRLF,Options) -> forward(Subject,A,1,Unicode,CRLF) end, {match,MM} = loopexec(Subject,RE,NewA,Y, - Unicode,CRLF,Options), + Unicode,CRLF,Options,false), case M of [] -> {match,MM}; diff --git a/lib/stdlib/src/stdlib.app.src b/lib/stdlib/src/stdlib.app.src index ecb514e9f3..e6c42b9aac 100644 --- a/lib/stdlib/src/stdlib.app.src +++ b/lib/stdlib/src/stdlib.app.src @@ -108,7 +108,7 @@ dets]}, {applications, [kernel]}, {env, []}, - {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-10.4","crypto-3.3", + {runtime_dependencies, ["sasl-3.0","kernel-6.0","erts-@OTP-15831:OTP-15836:OTP-15889@","crypto-3.3", "compiler-5.0"]} ]}. diff --git a/lib/stdlib/test/ets_SUITE.erl b/lib/stdlib/test/ets_SUITE.erl index dd49288417..09238ae2b4 100644 --- a/lib/stdlib/test/ets_SUITE.erl +++ b/lib/stdlib/test/ets_SUITE.erl @@ -75,7 +75,8 @@ -export([throughput_benchmark/0, throughput_benchmark/1, test_throughput_benchmark/1, - long_throughput_benchmark/1]). + long_throughput_benchmark/1, + lookup_catree_par_vs_seq_init_benchmark/0]). -export([exit_large_table_owner/1, exit_many_large_table_owner/1, exit_many_tables_owner/1, @@ -6728,6 +6729,14 @@ do_work(WorksDoneSoFar, Table, ProbHelpTab, Range, Operations) -> end. prefill_table(T, KeyRange, Num, ObjFun) -> + Parent = self(), + spawn_link(fun() -> + prefill_table_helper(T, KeyRange, Num, ObjFun), + Parent ! done + end), + receive done -> ok end. + +prefill_table_helper(T, KeyRange, Num, ObjFun) -> Seed = rand:uniform(KeyRange), %%io:format("prefill_table: Seed = ~p\n", [Seed]), RState = unique_rand_start(KeyRange, Seed), @@ -6740,11 +6749,77 @@ prefill_table_loop(T, RS0, N, ObjFun) -> ets:insert(T, ObjFun(Key)), prefill_table_loop(T, RS1, N-1, ObjFun). +inserter_proc_starter(T, ToInsert, Parent) -> + receive + start -> ok + end, + inserter_proc(T, ToInsert, [], Parent, false). + +inserter_proc(T, [], Inserted, Parent, _) -> + inserter_proc(T, Inserted, [], Parent, true); +inserter_proc(T, [I | ToInsert], Inserted, Parent, CanStop) -> + Stop = + case CanStop of + true -> + receive + stop -> Parent ! stopped + after 0 -> no_stop + end; + false -> no_stop + end, + case Stop of + no_stop -> + ets:insert(T, I), + inserter_proc(T, ToInsert, [I | Inserted], Parent, CanStop); + _ -> ok + end. + +prefill_table_parallel(T, KeyRange, Num, ObjFun) -> + Parent = self(), + spawn_link(fun() -> + prefill_table_parallel_helper(T, KeyRange, Num, ObjFun), + Parent ! done + end), + receive done -> ok end. + +prefill_table_parallel_helper(T, KeyRange, Num, ObjFun) -> + NrOfSchedulers = erlang:system_info(schedulers), + Seed = rand:uniform(KeyRange), + %%io:format("prefill_table: Seed = ~p\n", [Seed]), + RState = unique_rand_start(KeyRange, Seed), + InsertMap = prefill_insert_map_loop(T, RState, Num, ObjFun, #{}, NrOfSchedulers), + Self = self(), + Pids = [ + begin + InserterFun = + fun() -> + inserter_proc_starter(T, ToInsert, Self) + end, + spawn_link(InserterFun) + end + || ToInsert <- maps:values(InsertMap)], + [Pid ! start || Pid <- Pids], + timer:sleep(1000), + [Pid ! stop || Pid <- Pids], + [receive stopped -> ok end || _Pid <- Pids]. + +prefill_insert_map_loop(_, _, 0, _, InsertMap, _NrOfSchedulers) -> + InsertMap; +prefill_insert_map_loop(T, RS0, N, ObjFun, InsertMap, NrOfSchedulers) -> + {Key, RS1} = unique_rand_next(RS0), + Sched = N rem NrOfSchedulers, + PrevInserts = maps:get(Sched, InsertMap, []), + NewPrevInserts = [ObjFun(Key) | PrevInserts], + NewInsertMap = maps:put(Sched, NewPrevInserts, InsertMap), + prefill_insert_map_loop(T, RS1, N-1, ObjFun, NewInsertMap, NrOfSchedulers). + -record(ets_throughput_bench_config, {benchmark_duration_ms = 3000, recover_time_ms = 1000, thread_counts = not_set, key_ranges = [1000000], + init_functions = [fun prefill_table/4], + nr_of_repeats = 1, scenarios = [ [ @@ -6838,7 +6913,7 @@ prefill_table_loop(T, RS0, N, ObjFun) -> notify_res_fun = fun(_Name, _Throughput) -> ok end, print_result_paths_fun = fun(ResultPath, _LatestResultPath) -> - Comment = + Comment = io_lib:format("<a href=\"file:///~s\">Result visualization</a>",[ResultPath]), {comment, Comment} end @@ -6848,7 +6923,7 @@ stdout_notify_res(ResultPath, LatestResultPath) -> io:format("Result Location: /~s~n", [ResultPath]), io:format("Latest Result Location: ~s~n", [LatestResultPath]). -throughput_benchmark() -> +throughput_benchmark() -> throughput_benchmark( #ets_throughput_bench_config{ print_result_paths_fun = fun stdout_notify_res/2}). @@ -6856,9 +6931,11 @@ throughput_benchmark() -> throughput_benchmark( #ets_throughput_bench_config{ benchmark_duration_ms = BenchmarkDurationMs, - recover_time_ms = RecoverTimeMs, - thread_counts = ThreadCountsOpt, - key_ranges = KeyRanges, + recover_time_ms = RecoverTimeMs, + thread_counts = ThreadCountsOpt, + key_ranges = KeyRanges, + init_functions = InitFuns, + nr_of_repeats = NrOfRepeats, scenarios = Scenarios, table_types = TableTypes, etsmem_fun = ETSMemFun, @@ -6872,21 +6949,21 @@ throughput_benchmark( Start = rand:uniform(KeyRange), Last = lists:foldl( - fun(_, Prev) -> + fun(_, Prev) -> case Prev of '$end_of_table'-> ok; _ -> try ets:next(T, Prev) of Normal -> Normal catch - error:badarg -> + error:badarg -> % sets (not ordered_sets) cannot handle when the argument % to next is not in the set rand:uniform(KeyRange) end end end, - Start, + Start, lists:seq(1, SeqSize)), case Last =:= -1 of true -> io:format("Will never be printed"); @@ -6898,26 +6975,26 @@ throughput_benchmark( Start = rand:uniform(KeyRange), Last = Start + SeqSize, case -1 =:= ets:select_count(T, - ets:fun2ms(fun({X}) when X > Start andalso X =< Last -> true end)) of + ets:fun2ms(fun({X}) when X > Start andalso X =< Last -> true end)) of true -> io:format("Will never be printed"); false -> ok end end, %% Mapping benchmark operation names to their corresponding functions that do them - Operations = + Operations = #{insert => - fun(T,KeyRange) -> + fun(T,KeyRange) -> Num = rand:uniform(KeyRange), ets:insert(T, {Num}) end, delete => - fun(T,KeyRange) -> + fun(T,KeyRange) -> Num = rand:uniform(KeyRange), ets:delete(T, Num) end, lookup => - fun(T,KeyRange) -> + fun(T,KeyRange) -> Num = rand:uniform(KeyRange), ets:lookup(T, Num) end, @@ -6928,8 +7005,8 @@ throughput_benchmark( nextseq1000 => fun(T,KeyRange) -> NextSeqOp(T,KeyRange,1000) end, selectAll => - fun(T,_KeyRange) -> - case -1 =:= ets:select_count(T, ets:fun2ms(fun(_X) -> true end)) of + fun(T,_KeyRange) -> + case -1 =:= ets:select_count(T, ets:fun2ms(fun(_X) -> true end)) of true -> io:format("Will never be printed"); false -> ok end @@ -6951,7 +7028,7 @@ throughput_benchmark( NewCurrent = Current + OpPropability, [{NewCurrent, OpName}| Calculate(Res, NewCurrent)] end, - RenderScenario = + RenderScenario = fun R([], StringSoFar) -> StringSoFar; R([{Fraction, Operation}], StringSoFar) -> @@ -6978,7 +7055,7 @@ throughput_benchmark( false -> ok end end, - DataHolder = + DataHolder = fun DataHolderFun(Data)-> receive {get_data, Pid} -> Pid ! {ets_bench_data, Data}; @@ -6992,18 +7069,21 @@ throughput_benchmark( DataHolderPid ! io_lib:format(Str, List) end, GetData = - fun () -> + fun () -> DataHolderPid ! {get_data, self()}, receive {ets_bench_data, Data} -> Data end end, %% Function that runs a benchmark instance and returns the number %% of operations that were performed RunBenchmark = - fun({NrOfProcs, TableConfig, Scenario, Range, Duration}) -> + fun({NrOfProcs, TableConfig, Scenario, Range, Duration, InitFun}) -> ProbHelpTab = CalculateOpsProbHelpTab(Scenario, 0), Table = ets:new(t, TableConfig), Nobj = Range div 2, - prefill_table(Table, Range, Nobj, fun(K) -> {K} end), + case InitFun of + not_set -> prefill_table(Table, Range, Nobj, fun(K) -> {K} end); + _ -> InitFun(Table, Range, Nobj, fun(K) -> {K} end) + end, Nobj = ets:info(Table, size), SafeFixTableIfRequired(Table, Scenario, true), ParentPid = self(), @@ -7016,12 +7096,14 @@ throughput_benchmark( end, ChildPids = lists:map(fun(_N) ->spawn_link(Worker)end, lists:seq(1, NrOfProcs)), + erlang:garbage_collect(), + timer:sleep(RecoverTimeMs), lists:foreach(fun(Pid) -> Pid ! start end, ChildPids), timer:sleep(Duration), lists:foreach(fun(Pid) -> Pid ! stop end, ChildPids), TotalWorksDone = lists:foldl( - fun(_, Sum) -> - receive + fun(_, Sum) -> + receive Count -> Sum + Count end end, 0, ChildPids), @@ -7032,27 +7114,32 @@ throughput_benchmark( RunBenchmarkInSepProcess = fun(ParameterTuple) -> P = self(), - spawn_link(fun()-> P ! {bench_result, RunBenchmark(ParameterTuple)} end), - Result = receive {bench_result, Res} -> Res end, - timer:sleep(RecoverTimeMs), - Result + Results = + [begin + spawn_link(fun()-> P ! {bench_result, RunBenchmark(ParameterTuple)} end), + receive {bench_result, Res} -> Res end + end || _ <- lists:seq(1, NrOfRepeats)], + lists:sum(Results) / NrOfRepeats end, RunBenchmarkAndReport = fun(ThreadCount, TableType, Scenario, KeyRange, - Duration) -> + Duration, + InitFunName, + InitFun) -> Result = RunBenchmarkInSepProcess({ThreadCount, TableType, Scenario, KeyRange, - Duration}), + Duration, + InitFun}), Throughput = Result/(Duration/1000.0), PrintData("; ~f",[Throughput]), - Name = io_lib:format("Scenario: ~w, Key Range Size: ~w, " + Name = io_lib:format("Scenario: ~s, ~w, Key Range Size: ~w, " "# of Processes: ~w, Table Type: ~w", - [Scenario, KeyRange, ThreadCount, TableType]), + [InitFunName, Scenario, KeyRange, ThreadCount, TableType]), NotifyResFun(Name, Throughput) end, ThreadCounts = @@ -7087,17 +7174,29 @@ throughput_benchmark( PrintData("$~n",[]), lists:foreach( fun(TableType) -> - PrintData("~w ",[TableType]), lists:foreach( - fun(ThreadCount) -> - RunBenchmarkAndReport(ThreadCount, - TableType, - Scenario, - KeyRange, - BenchmarkDurationMs) + fun(InitFunArg) -> + {InitFunName, InitFun} = + case InitFunArg of + {FunName, Fun} -> {FunName, Fun}; + Fun -> {"", Fun} + end, + PrintData("~s,~w ",[InitFunName,TableType]), + lists:foreach( + fun(ThreadCount) -> + RunBenchmarkAndReport(ThreadCount, + TableType, + Scenario, + KeyRange, + BenchmarkDurationMs, + InitFunName, + InitFun) + end, + ThreadCounts), + PrintData("$~n",[]) end, - ThreadCounts), - PrintData("$~n",[]) + InitFuns) + end, TableTypes) end, @@ -7121,7 +7220,7 @@ throughput_benchmark( test_throughput_benchmark(Config) when is_list(Config) -> throughput_benchmark( #ets_throughput_bench_config{ - benchmark_duration_ms = 100, + benchmark_duration_ms = 100, recover_time_ms = 0, thread_counts = [1, erlang:system_info(schedulers)], key_ranges = [50000], @@ -7136,7 +7235,7 @@ long_throughput_benchmark(Config) when is_list(Config) -> recover_time_ms = 1000, thread_counts = [1, N div 2, N], key_ranges = [1000000], - scenarios = + scenarios = [ [ {0.5, insert}, @@ -7171,15 +7270,15 @@ long_throughput_benchmark(Config) when is_list(Config) -> {0.01, partial_select1000} ] ], - table_types = + table_types = [ [ordered_set, public, {write_concurrency, true}, {read_concurrency, true}], [set, public, {write_concurrency, true}, {read_concurrency, true}] ], etsmem_fun = fun etsmem/0, verify_etsmem_fun = fun verify_etsmem/1, - notify_res_fun = - fun(Name, Throughput) -> + notify_res_fun = + fun(Name, Throughput) -> SummaryTable = proplists:get_value(ets_benchmark_result_summary_tab, Config), AddToSummaryCounter = @@ -7209,13 +7308,47 @@ long_throughput_benchmark(Config) when is_list(Config) -> total_throughput_ordered_set) end, ct_event:notify( - #event{name = benchmark_data, + #event{name = benchmark_data, data = [{suite,"ets_bench"}, {name, Name}, {value,Throughput}]}) end }). +%% This function compares the lookup operation's performance for +%% ordered_set ETS tables with and without write_concurrency enabled +%% when the data structures have been populated in parallel and +%% sequentially. +%% +%% The main purpose of this function is to check that the +%% implementation of ordered_set with write_concurrency (CA tree) +%% adapts its structure to contention even when only lookup operations +%% are used. +lookup_catree_par_vs_seq_init_benchmark() -> + N = erlang:system_info(schedulers), + throughput_benchmark( + #ets_throughput_bench_config{ + benchmark_duration_ms = 600000, + recover_time_ms = 1000, + thread_counts = [1, N div 2, N], + key_ranges = [1000000], + init_functions = [{"seq_init", fun prefill_table/4}, + {"par_init", fun prefill_table_parallel/4}], + nr_of_repeats = 1, + scenarios = + [ + [ + {1.0, lookup} + ] + ], + table_types = + [ + [ordered_set, public, {write_concurrency, true}], + [ordered_set, public] + ], + print_result_paths_fun = fun stdout_notify_res/2 + }). + add_lists(L1,L2) -> add_lists(L1,L2,[]). add_lists([],[],Acc) -> diff --git a/lib/stdlib/test/re_SUITE.erl b/lib/stdlib/test/re_SUITE.erl index c9ef9da990..06d8fe9255 100644 --- a/lib/stdlib/test/re_SUITE.erl +++ b/lib/stdlib/test/re_SUITE.erl @@ -28,7 +28,8 @@ pcre_compile_workspace_overflow/1,re_infinite_loop/1, re_backwards_accented/1,opt_dupnames/1,opt_all_names/1,inspect/1, opt_no_start_optimize/1,opt_never_utf/1,opt_ucp/1, - match_limit/1,sub_binaries/1,copt/1]). + match_limit/1,sub_binaries/1,copt/1,global_unicode_validation/1, + yield_on_subject_validation/1]). -include_lib("common_test/include/ct.hrl"). -include_lib("kernel/include/file.hrl"). @@ -45,7 +46,8 @@ all() -> pcre_compile_workspace_overflow, re_infinite_loop, re_backwards_accented, opt_dupnames, opt_all_names, inspect, opt_no_start_optimize,opt_never_utf,opt_ucp, - match_limit, sub_binaries, re_version]. + match_limit, sub_binaries, re_version, global_unicode_validation, + yield_on_subject_validation]. groups() -> []. @@ -200,7 +202,58 @@ re_version(_Config) -> {match,[Version]} = re:run(Version,"^[0-9]\\.[0-9]{2} 20[0-9]{2}-[0-9]{2}-[0-9]{2}",[{capture,all,binary}]), ok. +global_unicode_validation(Config) when is_list(Config) -> + %% Test that unicode validation of the subject is not done + %% for every match found... + Bin = binary:copy(<<"abc\n">>,100000), + {TimeAscii, _} = take_time(fun () -> + re:run(Bin, <<"b">>, [global]) + end), + {TimeUnicode, _} = take_time(fun () -> + re:run(Bin, <<"b">>, [unicode,global]) + end), + if TimeAscii == 0; TimeUnicode == 0 -> + {comment, "Not good enough resolution to compare results"}; + true -> + %% The time the operations takes should be in the + %% same order of magnitude. If validation of the + %% whole subject occurs for every match, the unicode + %% variant will take way longer time... + true = TimeUnicode div TimeAscii < 10 + end. + +take_time(Fun) -> + Start = erlang:monotonic_time(nanosecond), + Res = Fun(), + End = erlang:monotonic_time(nanosecond), + {End-Start, Res}. + +yield_on_subject_validation(Config) when is_list(Config) -> + Go = make_ref(), + Bin = binary:copy(<<"abc\n">>,100000), + {P, M} = spawn_opt(fun () -> + receive Go -> ok end, + {match,[{1,1}]} = re:run(Bin, <<"b">>, [unicode]) + end, + [link, monitor]), + 1 = erlang:trace(P, true, [running]), + P ! Go, + N = count_re_run_trap_out(P, M), + true = N >= 5, + ok. +count_re_run_trap_out(P, M) when is_reference(M) -> + receive {'DOWN',M,process,P,normal} -> ok end, + TD = erlang:trace_delivered(P), + receive {trace_delivered, P, TD} -> ok end, + count_re_run_trap_out(P, 0); +count_re_run_trap_out(P, N) when is_integer(N) -> + receive + {trace,P,out,{erlang,re_run_trap,3}} -> + count_re_run_trap_out(P, N+1) + after 0 -> + N + end. %% Test compile options given directly to run. combined_options(Config) when is_list(Config) -> diff --git a/lib/stdlib/test/re_SUITE_data/testoutput1 b/lib/stdlib/test/re_SUITE_data/testoutput1 index eff8ecc948..e6147e60b9 100644 --- a/lib/stdlib/test/re_SUITE_data/testoutput1 +++ b/lib/stdlib/test/re_SUITE_data/testoutput1 @@ -9446,4 +9446,28 @@ No match >XXX< 0: X +/ (?<word> \w+ )* \. /xi + pokus. + 0: pokus. + 1: pokus + +/(?(DEFINE) (?<word> \w+ ) ) (?&word)* \./xi + pokus. + 0: pokus. + +/(?(DEFINE) (?<word> \w+ ) ) ( (?&word)* ) \./xi + pokus. + 0: pokus. + 1: <unset> + 2: pokus + +/(?&word)* (?(DEFINE) (?<word> \w+ ) ) \./xi + pokus. + 0: pokus. + +/(?&word)* \. (?<word> \w+ )/xi + pokus.hokus + 0: pokus.hokus + 1: hokus + /-- End of testinput1 --/ diff --git a/lib/stdlib/test/re_SUITE_data/testoutput2 b/lib/stdlib/test/re_SUITE_data/testoutput2 index 61ed8d9d4e..4ccda27201 100644 --- a/lib/stdlib/test/re_SUITE_data/testoutput2 +++ b/lib/stdlib/test/re_SUITE_data/testoutput2 @@ -14721,4 +14721,8 @@ No need char 0: ab 1: a +/(?(?=^))b/ + abc + 0: b + /-- End of testinput2 --/ diff --git a/lib/stdlib/test/re_SUITE_data/testoutput4 b/lib/stdlib/test/re_SUITE_data/testoutput4 index d43c12392d..69e812cd35 100644 --- a/lib/stdlib/test/re_SUITE_data/testoutput4 +++ b/lib/stdlib/test/re_SUITE_data/testoutput4 @@ -1277,4 +1277,8 @@ No match \\C(\\W?Å¿)'?{{ No match +/[^\x{100}-\x{ffff}]*[\x80-\xff]/8 + \x{99}\x{99}\x{99} + 0: \x{99}\x{99}\x{99} + /-- End of testinput4 --/ diff --git a/lib/stdlib/test/re_testoutput1_replacement_test.erl b/lib/stdlib/test/re_testoutput1_replacement_test.erl index f14df547ef..bb43047757 100644 --- a/lib/stdlib/test/re_testoutput1_replacement_test.erl +++ b/lib/stdlib/test/re_testoutput1_replacement_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2017. All Rights Reserved. +%% Copyright Ericsson AB 2008-2019. 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. @@ -20014,4 +20014,31 @@ run56() -> <<" MumdPEeFred:099">> = iolist_to_binary(re:replace(" Fred:099","(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\\s])","\\1&M&umdPE&e",[global])), <<" ugxGKrBXEcHyG">> = iolist_to_binary(re:replace(" X","(?=.*X)X$","ugxGKrB&EcHyG",[])), <<" ugxGKrBXEcHyG">> = iolist_to_binary(re:replace(" X","(?=.*X)X$","ugxGKrB&EcHyG",[global])), + <<">MHFvXX<">> = iolist_to_binary(re:replace(">XXX<","X+(?#comment)?","MHFv",[])), + <<">MHFvMHFvMHFv<">> = iolist_to_binary(re:replace(">XXX<","X+(?#comment)?","MHFv",[global])), + <<"lTpokusldxfHXOpokuswsrRorpokus.">> = iolist_to_binary(re:replace("pokus."," (?<word> \\w+ )* \\. ","lT\\1ldxfHXO\\1wsrRor&",[extended, + caseless])), + <<"lTpokusldxfHXOpokuswsrRorpokus.">> = iolist_to_binary(re:replace("pokus."," (?<word> \\w+ )* \\. ","lT\\1ldxfHXO\\1wsrRor&",[extended, + caseless, + global])), + <<"Oeapokus.xo">> = iolist_to_binary(re:replace("pokus.","(?(DEFINE) (?<word> \\w+ ) ) (?&word)* \\.","Oea&xo",[extended, + caseless])), + <<"Oeapokus.xo">> = iolist_to_binary(re:replace("pokus.","(?(DEFINE) (?<word> \\w+ ) ) (?&word)* \\.","Oea&xo",[extended, + caseless, + global])), + <<"Wpokus.pity">> = iolist_to_binary(re:replace("pokus.","(?(DEFINE) (?<word> \\w+ ) ) ( (?&word)* ) \\.","W&pity",[extended, + caseless])), + <<"Wpokus.pity">> = iolist_to_binary(re:replace("pokus.","(?(DEFINE) (?<word> \\w+ ) ) ( (?&word)* ) \\.","W&pity",[extended, + caseless, + global])), + <<"iujmNtBvmcyi">> = iolist_to_binary(re:replace("pokus.","(?&word)* (?(DEFINE) (?<word> \\w+ ) ) \\.","iuj\\1m\\1NtBvmcyi\\1",[extended, + caseless])), + <<"iujmNtBvmcyi">> = iolist_to_binary(re:replace("pokus.","(?&word)* (?(DEFINE) (?<word> \\w+ ) ) \\.","iuj\\1m\\1NtBvmcyi\\1",[extended, + caseless, + global])), + <<"Ipokus.hokusbQpokus.hokusB">> = iolist_to_binary(re:replace("pokus.hokus","(?&word)* \\. (?<word> \\w+ )","I&bQ&B",[extended, + caseless])), + <<"Ipokus.hokusbQpokus.hokusB">> = iolist_to_binary(re:replace("pokus.hokus","(?&word)* \\. (?<word> \\w+ )","I&bQ&B",[extended, + caseless, + global])), ok. diff --git a/lib/stdlib/test/re_testoutput1_split_test.erl b/lib/stdlib/test/re_testoutput1_split_test.erl index 8218cd9bd2..fcffa89e3f 100644 --- a/lib/stdlib/test/re_testoutput1_split_test.erl +++ b/lib/stdlib/test/re_testoutput1_split_test.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2008-2017. All Rights Reserved. +%% Copyright Ericsson AB 2008-2019. 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. @@ -84,1360 +84,1360 @@ run() -> run56(), ok. run0() -> - <<"">> = iolist_to_binary(join(re:split("the quick brown fox","the quick brown fox",[trim]))), + <<"">> = iolist_to_binary(join(re:split("the quick brown fox","the quick brown fox",[trim]))), <<":">> = iolist_to_binary(join(re:split("the quick brown fox","the quick brown fox",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("the quick brown fox","the quick brown fox",[]))), - <<"The quick brown FOX">> = iolist_to_binary(join(re:split("The quick brown FOX","the quick brown fox",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("the quick brown fox","the quick brown fox",[]))), + <<"The quick brown FOX">> = iolist_to_binary(join(re:split("The quick brown FOX","the quick brown fox",[trim]))), <<"The quick brown FOX">> = iolist_to_binary(join(re:split("The quick brown FOX","the quick brown fox",[{parts, - 2}]))), - <<"The quick brown FOX">> = iolist_to_binary(join(re:split("The quick brown FOX","the quick brown fox",[]))), - <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","the quick brown fox",[trim]))), + 2}]))), + <<"The quick brown FOX">> = iolist_to_binary(join(re:split("The quick brown FOX","the quick brown fox",[]))), + <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","the quick brown fox",[trim]))), <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","the quick brown fox",[{parts, - 2}]))), - <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","the quick brown fox",[]))), - <<"What do you know about THE QUICK BROWN FOX?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","the quick brown fox",[trim]))), + 2}]))), + <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","the quick brown fox",[]))), + <<"What do you know about THE QUICK BROWN FOX?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","the quick brown fox",[trim]))), <<"What do you know about THE QUICK BROWN FOX?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","the quick brown fox",[{parts, - 2}]))), - <<"What do you know about THE QUICK BROWN FOX?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","the quick brown fox",[]))), + 2}]))), + <<"What do you know about THE QUICK BROWN FOX?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","the quick brown fox",[]))), <<"">> = iolist_to_binary(join(re:split("the quick brown fox","The quick brown fox",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("the quick brown fox","The quick brown fox",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("the quick brown fox","The quick brown fox",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("the quick brown fox","The quick brown fox",[caseless]))), <<"">> = iolist_to_binary(join(re:split("The quick brown FOX","The quick brown fox",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("The quick brown FOX","The quick brown fox",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("The quick brown FOX","The quick brown fox",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("The quick brown FOX","The quick brown fox",[caseless]))), <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","The quick brown fox",[caseless, - trim]))), + trim]))), <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","The quick brown fox",[caseless, {parts, - 2}]))), - <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","The quick brown fox",[caseless]))), + 2}]))), + <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about the quick brown fox?","The quick brown fox",[caseless]))), <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","The quick brown fox",[caseless, - trim]))), + trim]))), <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","The quick brown fox",[caseless, {parts, - 2}]))), - <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","The quick brown fox",[caseless]))), + 2}]))), + <<"What do you know about :?">> = iolist_to_binary(join(re:split("What do you know about THE QUICK BROWN FOX?","The quick brown fox",[caseless]))), <<"">> = iolist_to_binary(join(re:split("abcd -
9;$\\?caxyz","abcd\\t\\n\\r\\f\\a\\e\\071\\x3b\\$\\\\\\?caxyz",[trim]))), +
9;$\\?caxyz","abcd\\t\\n\\r\\f\\a\\e\\071\\x3b\\$\\\\\\?caxyz",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcd
9;$\\?caxyz","abcd\\t\\n\\r\\f\\a\\e\\071\\x3b\\$\\\\\\?caxyz",[{parts, - 2}]))), + 2}]))), <<":">> = iolist_to_binary(join(re:split("abcd -
9;$\\?caxyz","abcd\\t\\n\\r\\f\\a\\e\\071\\x3b\\$\\\\\\?caxyz",[]))), - <<"">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), +
9;$\\?caxyz","abcd\\t\\n\\r\\f\\a\\e\\071\\x3b\\$\\\\\\?caxyz",[]))), + <<"">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("abxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("abxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("abxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aabxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aabxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aabxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("abcxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("abcxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aabcxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcxyzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aabcxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aabcxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabcxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabcxyzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABBzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABBzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABBzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABBzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<">>>">> = iolist_to_binary(join(re:split(">>>aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypABBzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<">>>">> = iolist_to_binary(join(re:split(">>>aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<">>>:">> = iolist_to_binary(join(re:split(">>>aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<">>>:">> = iolist_to_binary(join(re:split(">>>aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<">">> = iolist_to_binary(join(re:split(">aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<">>>:">> = iolist_to_binary(join(re:split(">>>aaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<">">> = iolist_to_binary(join(re:split(">aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<">:">> = iolist_to_binary(join(re:split(">aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<">:">> = iolist_to_binary(join(re:split(">aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<">>>>">> = iolist_to_binary(join(re:split(">>>>abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<">:">> = iolist_to_binary(join(re:split(">aaaabxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<">>>>">> = iolist_to_binary(join(re:split(">>>>abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<">>>>:">> = iolist_to_binary(join(re:split(">>>>abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<">>>>:">> = iolist_to_binary(join(re:split(">>>>abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<">>>>:">> = iolist_to_binary(join(re:split(">>>>abcxyzpqrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"abxyzpqrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"abxyzpqrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"abxyzpqrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"abxyzpqrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"abxyzpqrrrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<"abxyzpqrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"abxyzpqrrrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"abxyzpqrrrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"abxyzpqrrrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"abxyzpqrrrabxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrabxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<"abxyzpqrrrrabbxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrrabbxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"abxyzpqrrrabxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrabxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"abxyzpqrrrabxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrabxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"abxyzpqrrrabxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrabxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<"abxyzpqrrrabxyyyypqAzz">> = iolist_to_binary(join(re:split("abxyzpqrrrabxyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"aaaabcxyzzzzpqrrrabbbxyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<"aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyyyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"aaaabcxyzzzzpqrrrabbbxyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"aaaabcxyzzzzpqrrrabbbxyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"aaaabcxyzzzzpqrrrabbbxyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<"aaabcxyzpqrrrabbxyyyypqqqqqqqAzz">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), + 2}]))), + <<"aaaabcxyzzzzpqrrrabbbxyyypqAzz">> = iolist_to_binary(join(re:split("aaaabcxyzzzzpqrrrabbbxyyypqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<"aaabcxyzpqrrrabbxyyyypqqqqqqqAzz">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[trim]))), <<"aaabcxyzpqrrrabbxyyyypqqqqqqqAzz">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[{parts, - 2}]))), - <<"aaabcxyzpqrrrabbxyyyypqqqqqqqAzz">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abczz","^(abc){1,2}zz",[trim]))), + 2}]))), + <<"aaabcxyzpqrrrabbxyyyypqqqqqqqAzz">> = iolist_to_binary(join(re:split("aaabcxyzpqrrrabbxyyyypqqqqqqqAzz","a*abc?xyz+pqr{3}ab{2,}xy{4,5}pq{0,6}AB{0,}zz",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abczz","^(abc){1,2}zz",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abczz","^(abc){1,2}zz",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abczz","^(abc){1,2}zz",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcabczz","^(abc){1,2}zz",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abczz","^(abc){1,2}zz",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabczz","^(abc){1,2}zz",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabczz","^(abc){1,2}zz",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabczz","^(abc){1,2}zz",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(abc){1,2}zz",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabczz","^(abc){1,2}zz",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(abc){1,2}zz",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(abc){1,2}zz",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(abc){1,2}zz",[]))), - <<"zz">> = iolist_to_binary(join(re:split("zz","^(abc){1,2}zz",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(abc){1,2}zz",[]))), + <<"zz">> = iolist_to_binary(join(re:split("zz","^(abc){1,2}zz",[trim]))), <<"zz">> = iolist_to_binary(join(re:split("zz","^(abc){1,2}zz",[{parts, - 2}]))), - <<"zz">> = iolist_to_binary(join(re:split("zz","^(abc){1,2}zz",[]))), - <<"abcabcabczz">> = iolist_to_binary(join(re:split("abcabcabczz","^(abc){1,2}zz",[trim]))), + 2}]))), + <<"zz">> = iolist_to_binary(join(re:split("zz","^(abc){1,2}zz",[]))), + <<"abcabcabczz">> = iolist_to_binary(join(re:split("abcabcabczz","^(abc){1,2}zz",[trim]))), <<"abcabcabczz">> = iolist_to_binary(join(re:split("abcabcabczz","^(abc){1,2}zz",[{parts, - 2}]))), - <<"abcabcabczz">> = iolist_to_binary(join(re:split("abcabcabczz","^(abc){1,2}zz",[]))), - <<">>abczz">> = iolist_to_binary(join(re:split(">>abczz","^(abc){1,2}zz",[trim]))), + 2}]))), + <<"abcabcabczz">> = iolist_to_binary(join(re:split("abcabcabczz","^(abc){1,2}zz",[]))), + <<">>abczz">> = iolist_to_binary(join(re:split(">>abczz","^(abc){1,2}zz",[trim]))), <<">>abczz">> = iolist_to_binary(join(re:split(">>abczz","^(abc){1,2}zz",[{parts, - 2}]))), - <<">>abczz">> = iolist_to_binary(join(re:split(">>abczz","^(abc){1,2}zz",[]))), - <<":b">> = iolist_to_binary(join(re:split("bc","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<">>abczz">> = iolist_to_binary(join(re:split(">>abczz","^(abc){1,2}zz",[]))), + <<":b">> = iolist_to_binary(join(re:split("bc","^(b+?|a){1,2}?c",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("bc","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("bc","^(b+?|a){1,2}?c",[]))), - <<":b">> = iolist_to_binary(join(re:split("bbc","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("bc","^(b+?|a){1,2}?c",[]))), + <<":b">> = iolist_to_binary(join(re:split("bbc","^(b+?|a){1,2}?c",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("bbc","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("bbc","^(b+?|a){1,2}?c",[]))), - <<":bb">> = iolist_to_binary(join(re:split("bbbc","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("bbc","^(b+?|a){1,2}?c",[]))), + <<":bb">> = iolist_to_binary(join(re:split("bbbc","^(b+?|a){1,2}?c",[trim]))), <<":bb:">> = iolist_to_binary(join(re:split("bbbc","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":bb:">> = iolist_to_binary(join(re:split("bbbc","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":bb:">> = iolist_to_binary(join(re:split("bbbc","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("aac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("aac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aac","^(b+?|a){1,2}?c",[]))), - <<":bbbbbbbbbbb">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aac","^(b+?|a){1,2}?c",[]))), + <<":bbbbbbbbbbb">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+?|a){1,2}?c",[trim]))), <<":bbbbbbbbbbb:">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":bbbbbbbbbbb:">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":bbbbbbbbbbb:">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+?|a){1,2}?c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+?|a){1,2}?c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+?|a){1,2}?c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+?|a){1,2}?c",[]))), - <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+?|a){1,2}?c",[]))), + <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+?|a){1,2}?c",[trim]))), <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+?|a){1,2}?c",[]))), - <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+?|a){1,2}?c",[]))), + <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+?|a){1,2}?c",[trim]))), <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+?|a){1,2}?c",[]))), - <<":b">> = iolist_to_binary(join(re:split("bc","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+?|a){1,2}?c",[]))), + <<":b">> = iolist_to_binary(join(re:split("bc","^(b+|a){1,2}c",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("bc","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("bc","^(b+|a){1,2}c",[]))), - <<":bb">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("bc","^(b+|a){1,2}c",[]))), + <<":bb">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}c",[trim]))), <<":bb:">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":bb:">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}c",[]))), - <<":bbb">> = iolist_to_binary(join(re:split("bbbc","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":bb:">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}c",[]))), + <<":bbb">> = iolist_to_binary(join(re:split("bbbc","^(b+|a){1,2}c",[trim]))), <<":bbb:">> = iolist_to_binary(join(re:split("bbbc","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":bbb:">> = iolist_to_binary(join(re:split("bbbc","^(b+|a){1,2}c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":bbb:">> = iolist_to_binary(join(re:split("bbbc","^(b+|a){1,2}c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}c",[]))), - <<":a">> = iolist_to_binary(join(re:split("aac","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}c",[]))), + <<":a">> = iolist_to_binary(join(re:split("aac","^(b+|a){1,2}c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aac","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aac","^(b+|a){1,2}c",[]))), - <<":bbbbbbbbbbb">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aac","^(b+|a){1,2}c",[]))), + <<":bbbbbbbbbbb">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+|a){1,2}c",[trim]))), <<":bbbbbbbbbbb:">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":bbbbbbbbbbb:">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+|a){1,2}c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":bbbbbbbbbbb:">> = iolist_to_binary(join(re:split("abbbbbbbbbbbc","^(b+|a){1,2}c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+|a){1,2}c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+|a){1,2}c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+|a){1,2}c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbbbbbbbbbac","^(b+|a){1,2}c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+|a){1,2}c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+|a){1,2}c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+|a){1,2}c",[]))), - <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b+|a){1,2}c",[]))), + <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+|a){1,2}c",[trim]))), <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+|a){1,2}c",[{parts, - 2}]))), - <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+|a){1,2}c",[]))), - <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+|a){1,2}c",[trim]))), + 2}]))), + <<"aaac">> = iolist_to_binary(join(re:split("aaac","^(b+|a){1,2}c",[]))), + <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+|a){1,2}c",[trim]))), <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+|a){1,2}c",[{parts, - 2}]))), - <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+|a){1,2}c",[]))), - <<":b">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}?bc",[trim]))), + 2}]))), + <<"abbbbbbbbbbbac">> = iolist_to_binary(join(re:split("abbbbbbbbbbbac","^(b+|a){1,2}c",[]))), + <<":b">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}?bc",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}?bc",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}?bc",[]))), - <<":ba">> = iolist_to_binary(join(re:split("babc","^(b*|ba){1,2}?bc",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("bbc","^(b+|a){1,2}?bc",[]))), + <<":ba">> = iolist_to_binary(join(re:split("babc","^(b*|ba){1,2}?bc",[trim]))), <<":ba:">> = iolist_to_binary(join(re:split("babc","^(b*|ba){1,2}?bc",[{parts, - 2}]))), - <<":ba:">> = iolist_to_binary(join(re:split("babc","^(b*|ba){1,2}?bc",[]))), - <<":ba">> = iolist_to_binary(join(re:split("bbabc","^(b*|ba){1,2}?bc",[trim]))), + 2}]))), + <<":ba:">> = iolist_to_binary(join(re:split("babc","^(b*|ba){1,2}?bc",[]))), + <<":ba">> = iolist_to_binary(join(re:split("bbabc","^(b*|ba){1,2}?bc",[trim]))), <<":ba:">> = iolist_to_binary(join(re:split("bbabc","^(b*|ba){1,2}?bc",[{parts, - 2}]))), - <<":ba:">> = iolist_to_binary(join(re:split("bbabc","^(b*|ba){1,2}?bc",[]))), - <<":ba">> = iolist_to_binary(join(re:split("bababc","^(b*|ba){1,2}?bc",[trim]))), + 2}]))), + <<":ba:">> = iolist_to_binary(join(re:split("bbabc","^(b*|ba){1,2}?bc",[]))), + <<":ba">> = iolist_to_binary(join(re:split("bababc","^(b*|ba){1,2}?bc",[trim]))), <<":ba:">> = iolist_to_binary(join(re:split("bababc","^(b*|ba){1,2}?bc",[{parts, - 2}]))), - <<":ba:">> = iolist_to_binary(join(re:split("bababc","^(b*|ba){1,2}?bc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b*|ba){1,2}?bc",[trim]))), + 2}]))), + <<":ba:">> = iolist_to_binary(join(re:split("bababc","^(b*|ba){1,2}?bc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b*|ba){1,2}?bc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b*|ba){1,2}?bc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b*|ba){1,2}?bc",[]))), - <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(b*|ba){1,2}?bc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(b*|ba){1,2}?bc",[]))), + <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(b*|ba){1,2}?bc",[trim]))), <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(b*|ba){1,2}?bc",[{parts, - 2}]))), - <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(b*|ba){1,2}?bc",[]))), - <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(b*|ba){1,2}?bc",[trim]))), + 2}]))), + <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(b*|ba){1,2}?bc",[]))), + <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(b*|ba){1,2}?bc",[trim]))), <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(b*|ba){1,2}?bc",[{parts, - 2}]))), - <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(b*|ba){1,2}?bc",[]))), - <<":ba">> = iolist_to_binary(join(re:split("babc","^(ba|b*){1,2}?bc",[trim]))), + 2}]))), + <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(b*|ba){1,2}?bc",[]))), + <<":ba">> = iolist_to_binary(join(re:split("babc","^(ba|b*){1,2}?bc",[trim]))), <<":ba:">> = iolist_to_binary(join(re:split("babc","^(ba|b*){1,2}?bc",[{parts, - 2}]))), - <<":ba:">> = iolist_to_binary(join(re:split("babc","^(ba|b*){1,2}?bc",[]))), - <<":ba">> = iolist_to_binary(join(re:split("bbabc","^(ba|b*){1,2}?bc",[trim]))), + 2}]))), + <<":ba:">> = iolist_to_binary(join(re:split("babc","^(ba|b*){1,2}?bc",[]))), + <<":ba">> = iolist_to_binary(join(re:split("bbabc","^(ba|b*){1,2}?bc",[trim]))), <<":ba:">> = iolist_to_binary(join(re:split("bbabc","^(ba|b*){1,2}?bc",[{parts, - 2}]))), - <<":ba:">> = iolist_to_binary(join(re:split("bbabc","^(ba|b*){1,2}?bc",[]))), - <<":ba">> = iolist_to_binary(join(re:split("bababc","^(ba|b*){1,2}?bc",[trim]))), + 2}]))), + <<":ba:">> = iolist_to_binary(join(re:split("bbabc","^(ba|b*){1,2}?bc",[]))), + <<":ba">> = iolist_to_binary(join(re:split("bababc","^(ba|b*){1,2}?bc",[trim]))), <<":ba:">> = iolist_to_binary(join(re:split("bababc","^(ba|b*){1,2}?bc",[{parts, - 2}]))), - <<":ba:">> = iolist_to_binary(join(re:split("bababc","^(ba|b*){1,2}?bc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ba|b*){1,2}?bc",[trim]))), + 2}]))), + <<":ba:">> = iolist_to_binary(join(re:split("bababc","^(ba|b*){1,2}?bc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ba|b*){1,2}?bc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ba|b*){1,2}?bc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ba|b*){1,2}?bc",[]))), - <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(ba|b*){1,2}?bc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ba|b*){1,2}?bc",[]))), + <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(ba|b*){1,2}?bc",[trim]))), <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(ba|b*){1,2}?bc",[{parts, - 2}]))), - <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(ba|b*){1,2}?bc",[]))), - <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(ba|b*){1,2}?bc",[trim]))), + 2}]))), + <<"bababbc">> = iolist_to_binary(join(re:split("bababbc","^(ba|b*){1,2}?bc",[]))), + <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(ba|b*){1,2}?bc",[trim]))), <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(ba|b*){1,2}?bc",[{parts, - 2}]))), - <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(ba|b*){1,2}?bc",[]))), - <<"">> = iolist_to_binary(join(re:split(";z","^\\ca\\cA\\c[;\\c:",[trim]))), + 2}]))), + <<"babababc">> = iolist_to_binary(join(re:split("babababc","^(ba|b*){1,2}?bc",[]))), + <<"">> = iolist_to_binary(join(re:split(";z","^\\ca\\cA\\c[;\\c:",[trim]))), <<":">> = iolist_to_binary(join(re:split(";z","^\\ca\\cA\\c[;\\c:",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split(";z","^\\ca\\cA\\c[;\\c:",[]))), - <<":thing">> = iolist_to_binary(join(re:split("athing","^[ab\\]cde]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split(";z","^\\ca\\cA\\c[;\\c:",[]))), + <<":thing">> = iolist_to_binary(join(re:split("athing","^[ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("athing","^[ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("athing","^[ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("bthing","^[ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("athing","^[ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("bthing","^[ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("bthing","^[ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("bthing","^[ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("]thing","^[ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("bthing","^[ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("]thing","^[ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("]thing","^[ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("]thing","^[ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("cthing","^[ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("]thing","^[ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("cthing","^[ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("cthing","^[ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("cthing","^[ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("dthing","^[ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("cthing","^[ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("dthing","^[ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("dthing","^[ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("dthing","^[ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("ething","^[ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("dthing","^[ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("ething","^[ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("ething","^[ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("ething","^[ab\\]cde]",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("ething","^[ab\\]cde]",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[ab\\]cde]",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[ab\\]cde]",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[ab\\]cde]",[]))), - <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[ab\\]cde]",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[ab\\]cde]",[]))), + <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[ab\\]cde]",[trim]))), <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[ab\\]cde]",[{parts, - 2}]))), - <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[ab\\]cde]",[]))), - <<"[thing">> = iolist_to_binary(join(re:split("[thing","^[ab\\]cde]",[trim]))), + 2}]))), + <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[ab\\]cde]",[]))), + <<"[thing">> = iolist_to_binary(join(re:split("[thing","^[ab\\]cde]",[trim]))), <<"[thing">> = iolist_to_binary(join(re:split("[thing","^[ab\\]cde]",[{parts, - 2}]))), - <<"[thing">> = iolist_to_binary(join(re:split("[thing","^[ab\\]cde]",[]))), - <<"\\thing">> = iolist_to_binary(join(re:split("\\thing","^[ab\\]cde]",[trim]))), + 2}]))), + <<"[thing">> = iolist_to_binary(join(re:split("[thing","^[ab\\]cde]",[]))), + <<"\\thing">> = iolist_to_binary(join(re:split("\\thing","^[ab\\]cde]",[trim]))), <<"\\thing">> = iolist_to_binary(join(re:split("\\thing","^[ab\\]cde]",[{parts, - 2}]))), - <<"\\thing">> = iolist_to_binary(join(re:split("\\thing","^[ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("]thing","^[]cde]",[trim]))), + 2}]))), + <<"\\thing">> = iolist_to_binary(join(re:split("\\thing","^[ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("]thing","^[]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("]thing","^[]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("]thing","^[]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("cthing","^[]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("]thing","^[]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("cthing","^[]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("cthing","^[]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("cthing","^[]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("dthing","^[]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("cthing","^[]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("dthing","^[]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("dthing","^[]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("dthing","^[]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("ething","^[]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("dthing","^[]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("ething","^[]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("ething","^[]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("ething","^[]cde]",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("ething","^[]cde]",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[]cde]",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[]cde]",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[]cde]",[]))), - <<"athing">> = iolist_to_binary(join(re:split("athing","^[]cde]",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[]cde]",[]))), + <<"athing">> = iolist_to_binary(join(re:split("athing","^[]cde]",[trim]))), <<"athing">> = iolist_to_binary(join(re:split("athing","^[]cde]",[{parts, - 2}]))), - <<"athing">> = iolist_to_binary(join(re:split("athing","^[]cde]",[]))), - <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[]cde]",[trim]))), + 2}]))), + <<"athing">> = iolist_to_binary(join(re:split("athing","^[]cde]",[]))), + <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[]cde]",[trim]))), <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[]cde]",[{parts, - 2}]))), - <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<"fthing">> = iolist_to_binary(join(re:split("fthing","^[]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("[thing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("[thing","^[^ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("[thing","^[^ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("[thing","^[^ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("\\thing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("[thing","^[^ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("\\thing","^[^ab\\]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("\\thing","^[^ab\\]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("\\thing","^[^ab\\]cde]",[]))), - <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^ab\\]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("\\thing","^[^ab\\]cde]",[]))), + <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^ab\\]cde]",[trim]))), <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^ab\\]cde]",[{parts, - 2}]))), - <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^ab\\]cde]",[]))), - <<"athing">> = iolist_to_binary(join(re:split("athing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^ab\\]cde]",[]))), + <<"athing">> = iolist_to_binary(join(re:split("athing","^[^ab\\]cde]",[trim]))), <<"athing">> = iolist_to_binary(join(re:split("athing","^[^ab\\]cde]",[{parts, - 2}]))), - <<"athing">> = iolist_to_binary(join(re:split("athing","^[^ab\\]cde]",[]))), - <<"bthing">> = iolist_to_binary(join(re:split("bthing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<"athing">> = iolist_to_binary(join(re:split("athing","^[^ab\\]cde]",[]))), + <<"bthing">> = iolist_to_binary(join(re:split("bthing","^[^ab\\]cde]",[trim]))), <<"bthing">> = iolist_to_binary(join(re:split("bthing","^[^ab\\]cde]",[{parts, - 2}]))), - <<"bthing">> = iolist_to_binary(join(re:split("bthing","^[^ab\\]cde]",[]))), - <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<"bthing">> = iolist_to_binary(join(re:split("bthing","^[^ab\\]cde]",[]))), + <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^ab\\]cde]",[trim]))), <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^ab\\]cde]",[{parts, - 2}]))), - <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^ab\\]cde]",[]))), - <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^ab\\]cde]",[]))), + <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^ab\\]cde]",[trim]))), <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^ab\\]cde]",[{parts, - 2}]))), - <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^ab\\]cde]",[]))), - <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^ab\\]cde]",[trim]))), + 2}]))), + <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^ab\\]cde]",[]))), + <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^ab\\]cde]",[trim]))), <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^ab\\]cde]",[{parts, - 2}]))), - <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^ab\\]cde]",[]))), - <<"ething">> = iolist_to_binary(join(re:split("ething","^[^ab\\]cde]",[trim]))), + 2}]))), + <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^ab\\]cde]",[]))), + <<"ething">> = iolist_to_binary(join(re:split("ething","^[^ab\\]cde]",[trim]))), <<"ething">> = iolist_to_binary(join(re:split("ething","^[^ab\\]cde]",[{parts, - 2}]))), - <<"ething">> = iolist_to_binary(join(re:split("ething","^[^ab\\]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("athing","^[^]cde]",[trim]))), + 2}]))), + <<"ething">> = iolist_to_binary(join(re:split("ething","^[^ab\\]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("athing","^[^]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("athing","^[^]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("athing","^[^]cde]",[]))), - <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("athing","^[^]cde]",[]))), + <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^]cde]",[trim]))), <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^]cde]",[{parts, - 2}]))), - <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^]cde]",[]))), - <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^]cde]",[trim]))), + 2}]))), + <<":thing">> = iolist_to_binary(join(re:split("fthing","^[^]cde]",[]))), + <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^]cde]",[trim]))), <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^]cde]",[{parts, - 2}]))), - <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^]cde]",[]))), - <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^]cde]",[trim]))), + 2}]))), + <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[^]cde]",[]))), + <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^]cde]",[trim]))), <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^]cde]",[{parts, - 2}]))), - <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^]cde]",[]))), - <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^]cde]",[trim]))), + 2}]))), + <<"]thing">> = iolist_to_binary(join(re:split("]thing","^[^]cde]",[]))), + <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^]cde]",[trim]))), <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^]cde]",[{parts, - 2}]))), - <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^]cde]",[]))), - <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^]cde]",[trim]))), + 2}]))), + <<"cthing">> = iolist_to_binary(join(re:split("cthing","^[^]cde]",[]))), + <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^]cde]",[trim]))), <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^]cde]",[{parts, - 2}]))), - <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^]cde]",[]))), - <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[trim]))), + 2}]))), + <<"dthing">> = iolist_to_binary(join(re:split("dthing","^[^]cde]",[]))), + <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[trim]))), <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[{parts, - 2}]))), - <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[]))), - <<"">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[trim]))), + 2}]))), + <<"ething">> = iolist_to_binary(join(re:split("ething","^[^]cde]",[]))), + <<"">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("1","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("0","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("1","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("1","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("2","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("2","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("2","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("2","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("3","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("2","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("3","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("3","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("3","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("4","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("3","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("4","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("4","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("4","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("5","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("4","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("5","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("5","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("5","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("6","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("5","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("6","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("6","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("6","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("7","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("6","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("7","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("7","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("7","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("8","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("7","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("8","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("8","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("8","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("9","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("8","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("9","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("9","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("9","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("10","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("9","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("10","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("10","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("10","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("100","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("10","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("100","^[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("100","^[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("100","^[0-9]+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("100","^[0-9]+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[0-9]+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[0-9]+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[0-9]+$",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^[0-9]+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[0-9]+$",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^[0-9]+$",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^[0-9]+$",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("enter","^.*nter",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("enter","^.*nter",[trim]))), <<":">> = iolist_to_binary(join(re:split("enter","^.*nter",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("enter","^.*nter",[]))), - <<"">> = iolist_to_binary(join(re:split("inter","^.*nter",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("enter","^.*nter",[]))), + <<"">> = iolist_to_binary(join(re:split("inter","^.*nter",[trim]))), <<":">> = iolist_to_binary(join(re:split("inter","^.*nter",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("inter","^.*nter",[]))), - <<"">> = iolist_to_binary(join(re:split("uponter","^.*nter",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("inter","^.*nter",[]))), + <<"">> = iolist_to_binary(join(re:split("uponter","^.*nter",[trim]))), <<":">> = iolist_to_binary(join(re:split("uponter","^.*nter",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("uponter","^.*nter",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("uponter","^.*nter",[]))), ok. run1() -> - <<"">> = iolist_to_binary(join(re:split("xxx0","^xxx[0-9]+$",[trim]))), + <<"">> = iolist_to_binary(join(re:split("xxx0","^xxx[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("xxx0","^xxx[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("xxx0","^xxx[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("xxx1234","^xxx[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("xxx0","^xxx[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("xxx1234","^xxx[0-9]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("xxx1234","^xxx[0-9]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("xxx1234","^xxx[0-9]+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^xxx[0-9]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("xxx1234","^xxx[0-9]+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^xxx[0-9]+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^xxx[0-9]+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^xxx[0-9]+$",[]))), - <<"xxx">> = iolist_to_binary(join(re:split("xxx","^xxx[0-9]+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^xxx[0-9]+$",[]))), + <<"xxx">> = iolist_to_binary(join(re:split("xxx","^xxx[0-9]+$",[trim]))), <<"xxx">> = iolist_to_binary(join(re:split("xxx","^xxx[0-9]+$",[{parts, - 2}]))), - <<"xxx">> = iolist_to_binary(join(re:split("xxx","^xxx[0-9]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("x123","^.+[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<"xxx">> = iolist_to_binary(join(re:split("xxx","^xxx[0-9]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("x123","^.+[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("x123","^.+[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("x123","^.+[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("xx123","^.+[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("x123","^.+[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("xx123","^.+[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("xx123","^.+[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("xx123","^.+[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("123456","^.+[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("xx123","^.+[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("123456","^.+[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("123456","^.+[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("123456","^.+[0-9][0-9][0-9]$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("123456","^.+[0-9][0-9][0-9]$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+[0-9][0-9][0-9]$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+[0-9][0-9][0-9]$",[]))), - <<"123">> = iolist_to_binary(join(re:split("123","^.+[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+[0-9][0-9][0-9]$",[]))), + <<"123">> = iolist_to_binary(join(re:split("123","^.+[0-9][0-9][0-9]$",[trim]))), <<"123">> = iolist_to_binary(join(re:split("123","^.+[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<"123">> = iolist_to_binary(join(re:split("123","^.+[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("x1234","^.+[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<"123">> = iolist_to_binary(join(re:split("123","^.+[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("x1234","^.+[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("x1234","^.+[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("x1234","^.+[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("x123","^.+?[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("x1234","^.+[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("x123","^.+?[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("x123","^.+?[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("x123","^.+?[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("xx123","^.+?[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("x123","^.+?[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("xx123","^.+?[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("xx123","^.+?[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("xx123","^.+?[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("123456","^.+?[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("xx123","^.+?[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("123456","^.+?[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("123456","^.+?[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("123456","^.+?[0-9][0-9][0-9]$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+?[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("123456","^.+?[0-9][0-9][0-9]$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+?[0-9][0-9][0-9]$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+?[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+?[0-9][0-9][0-9]$",[]))), - <<"123">> = iolist_to_binary(join(re:split("123","^.+?[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.+?[0-9][0-9][0-9]$",[]))), + <<"123">> = iolist_to_binary(join(re:split("123","^.+?[0-9][0-9][0-9]$",[trim]))), <<"123">> = iolist_to_binary(join(re:split("123","^.+?[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<"123">> = iolist_to_binary(join(re:split("123","^.+?[0-9][0-9][0-9]$",[]))), - <<"">> = iolist_to_binary(join(re:split("x1234","^.+?[0-9][0-9][0-9]$",[trim]))), + 2}]))), + <<"123">> = iolist_to_binary(join(re:split("123","^.+?[0-9][0-9][0-9]$",[]))), + <<"">> = iolist_to_binary(join(re:split("x1234","^.+?[0-9][0-9][0-9]$",[trim]))), <<":">> = iolist_to_binary(join(re:split("x1234","^.+?[0-9][0-9][0-9]$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("x1234","^.+?[0-9][0-9][0-9]$",[]))), - <<":abc:pqr">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("x1234","^.+?[0-9][0-9][0-9]$",[]))), + <<":abc:pqr">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), <<":abc:pqr:">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[{parts, - 2}]))), - <<":abc:pqr:">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), + 2}]))), + <<":abc:pqr:">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), - <<"!pqr=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), + <<"!pqr=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), <<"!pqr=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[{parts, - 2}]))), - <<"!pqr=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), - <<"abc!=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), + 2}]))), + <<"!pqr=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("!pqr=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), + <<"abc!=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), <<"abc!=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[{parts, - 2}]))), - <<"abc!=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), - <<"abc!pqr=apquxz:ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz:ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), + 2}]))), + <<"abc!=apquxz.ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!=apquxz.ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), + <<"abc!pqr=apquxz:ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz:ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), <<"abc!pqr=apquxz:ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz:ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[{parts, - 2}]))), - <<"abc!pqr=apquxz:ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz:ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), - <<"abc!pqr=apquxz.ixr.zzz.ac.ukk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.ukk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), + 2}]))), + <<"abc!pqr=apquxz:ixr.zzz.ac.uk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz:ixr.zzz.ac.uk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), + <<"abc!pqr=apquxz.ixr.zzz.ac.ukk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.ukk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[trim]))), <<"abc!pqr=apquxz.ixr.zzz.ac.ukk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.ukk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[{parts, - 2}]))), - <<"abc!pqr=apquxz.ixr.zzz.ac.ukk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.ukk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), - <<"Well, we need a colon: somewhere">> = iolist_to_binary(join(re:split("Well, we need a colon: somewhere",":",[trim]))), + 2}]))), + <<"abc!pqr=apquxz.ixr.zzz.ac.ukk">> = iolist_to_binary(join(re:split("abc!pqr=apquxz.ixr.zzz.ac.ukk","^([^!]+)!(.+)=apquxz\\.ixr\\.zzz\\.ac\\.uk$",[]))), + <<"Well, we need a colon: somewhere">> = iolist_to_binary(join(re:split("Well, we need a colon: somewhere",":",[trim]))), <<"Well, we need a colon: somewhere">> = iolist_to_binary(join(re:split("Well, we need a colon: somewhere",":",[{parts, - 2}]))), - <<"Well, we need a colon: somewhere">> = iolist_to_binary(join(re:split("Well, we need a colon: somewhere",":",[]))), - <<"*** Fail if we don't">> = iolist_to_binary(join(re:split("*** Fail if we don't",":",[trim]))), + 2}]))), + <<"Well, we need a colon: somewhere">> = iolist_to_binary(join(re:split("Well, we need a colon: somewhere",":",[]))), + <<"*** Fail if we don't">> = iolist_to_binary(join(re:split("*** Fail if we don't",":",[trim]))), <<"*** Fail if we don't">> = iolist_to_binary(join(re:split("*** Fail if we don't",":",[{parts, - 2}]))), - <<"*** Fail if we don't">> = iolist_to_binary(join(re:split("*** Fail if we don't",":",[]))), + 2}]))), + <<"*** Fail if we don't">> = iolist_to_binary(join(re:split("*** Fail if we don't",":",[]))), <<":0abc">> = iolist_to_binary(join(re:split("0abc","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<":0abc:">> = iolist_to_binary(join(re:split("0abc","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<":0abc:">> = iolist_to_binary(join(re:split("0abc","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<":0abc:">> = iolist_to_binary(join(re:split("0abc","([\\da-f:]+)$",[caseless]))), <<":abc">> = iolist_to_binary(join(re:split("abc","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abc","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abc","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abc","([\\da-f:]+)$",[caseless]))), <<":fed">> = iolist_to_binary(join(re:split("fed","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<":fed:">> = iolist_to_binary(join(re:split("fed","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<":fed:">> = iolist_to_binary(join(re:split("fed","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<":fed:">> = iolist_to_binary(join(re:split("fed","([\\da-f:]+)$",[caseless]))), <<":E">> = iolist_to_binary(join(re:split("E","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<":E:">> = iolist_to_binary(join(re:split("E","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<":E:">> = iolist_to_binary(join(re:split("E","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<":E:">> = iolist_to_binary(join(re:split("E","([\\da-f:]+)$",[caseless]))), <<":::">> = iolist_to_binary(join(re:split("::","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"::::">> = iolist_to_binary(join(re:split("::","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"::::">> = iolist_to_binary(join(re:split("::","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"::::">> = iolist_to_binary(join(re:split("::","([\\da-f:]+)$",[caseless]))), <<":5f03:12C0::932e">> = iolist_to_binary(join(re:split("5f03:12C0::932e","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<":5f03:12C0::932e:">> = iolist_to_binary(join(re:split("5f03:12C0::932e","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<":5f03:12C0::932e:">> = iolist_to_binary(join(re:split("5f03:12C0::932e","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<":5f03:12C0::932e:">> = iolist_to_binary(join(re:split("5f03:12C0::932e","([\\da-f:]+)$",[caseless]))), <<"fed :def">> = iolist_to_binary(join(re:split("fed def","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"fed :def:">> = iolist_to_binary(join(re:split("fed def","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"fed :def:">> = iolist_to_binary(join(re:split("fed def","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"fed :def:">> = iolist_to_binary(join(re:split("fed def","([\\da-f:]+)$",[caseless]))), <<"Any old stu:ff">> = iolist_to_binary(join(re:split("Any old stuff","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"Any old stu:ff:">> = iolist_to_binary(join(re:split("Any old stuff","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"Any old stu:ff:">> = iolist_to_binary(join(re:split("Any old stuff","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"Any old stu:ff:">> = iolist_to_binary(join(re:split("Any old stuff","([\\da-f:]+)$",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","([\\da-f:]+)$",[caseless]))), <<"0zzz">> = iolist_to_binary(join(re:split("0zzz","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"0zzz">> = iolist_to_binary(join(re:split("0zzz","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"0zzz">> = iolist_to_binary(join(re:split("0zzz","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"0zzz">> = iolist_to_binary(join(re:split("0zzz","([\\da-f:]+)$",[caseless]))), <<"gzzz">> = iolist_to_binary(join(re:split("gzzz","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"gzzz">> = iolist_to_binary(join(re:split("gzzz","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"gzzz">> = iolist_to_binary(join(re:split("gzzz","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"gzzz">> = iolist_to_binary(join(re:split("gzzz","([\\da-f:]+)$",[caseless]))), <<"fed ">> = iolist_to_binary(join(re:split("fed ","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"fed ">> = iolist_to_binary(join(re:split("fed ","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"fed ">> = iolist_to_binary(join(re:split("fed ","([\\da-f:]+)$",[caseless]))), + 2}]))), + <<"fed ">> = iolist_to_binary(join(re:split("fed ","([\\da-f:]+)$",[caseless]))), <<"Any old rubbish">> = iolist_to_binary(join(re:split("Any old rubbish","([\\da-f:]+)$",[caseless, - trim]))), + trim]))), <<"Any old rubbish">> = iolist_to_binary(join(re:split("Any old rubbish","([\\da-f:]+)$",[caseless, {parts, - 2}]))), - <<"Any old rubbish">> = iolist_to_binary(join(re:split("Any old rubbish","([\\da-f:]+)$",[caseless]))), - <<":1:2:3">> = iolist_to_binary(join(re:split(".1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), + 2}]))), + <<"Any old rubbish">> = iolist_to_binary(join(re:split("Any old rubbish","([\\da-f:]+)$",[caseless]))), + <<":1:2:3">> = iolist_to_binary(join(re:split(".1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), <<":1:2:3:">> = iolist_to_binary(join(re:split(".1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[{parts, - 2}]))), - <<":1:2:3:">> = iolist_to_binary(join(re:split(".1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), - <<":12:123:0">> = iolist_to_binary(join(re:split("A.12.123.0","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), + 2}]))), + <<":1:2:3:">> = iolist_to_binary(join(re:split(".1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), + <<":12:123:0">> = iolist_to_binary(join(re:split("A.12.123.0","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), <<":12:123:0:">> = iolist_to_binary(join(re:split("A.12.123.0","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[{parts, - 2}]))), - <<":12:123:0:">> = iolist_to_binary(join(re:split("A.12.123.0","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), + 2}]))), + <<":12:123:0:">> = iolist_to_binary(join(re:split("A.12.123.0","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), - <<".1.2.3333">> = iolist_to_binary(join(re:split(".1.2.3333","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), + <<".1.2.3333">> = iolist_to_binary(join(re:split(".1.2.3333","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), <<".1.2.3333">> = iolist_to_binary(join(re:split(".1.2.3333","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[{parts, - 2}]))), - <<".1.2.3333">> = iolist_to_binary(join(re:split(".1.2.3333","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), - <<"1.2.3">> = iolist_to_binary(join(re:split("1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), + 2}]))), + <<".1.2.3333">> = iolist_to_binary(join(re:split(".1.2.3333","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), + <<"1.2.3">> = iolist_to_binary(join(re:split("1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), <<"1.2.3">> = iolist_to_binary(join(re:split("1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[{parts, - 2}]))), - <<"1.2.3">> = iolist_to_binary(join(re:split("1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), - <<"1234.2.3">> = iolist_to_binary(join(re:split("1234.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), + 2}]))), + <<"1.2.3">> = iolist_to_binary(join(re:split("1.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), + <<"1234.2.3">> = iolist_to_binary(join(re:split("1234.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[trim]))), <<"1234.2.3">> = iolist_to_binary(join(re:split("1234.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[{parts, - 2}]))), - <<"1234.2.3">> = iolist_to_binary(join(re:split("1234.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), - <<":1:non-sp1:non-sp2">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), + 2}]))), + <<"1234.2.3">> = iolist_to_binary(join(re:split("1234.2.3","^.*\\.(\\d{1,3})\\.(\\d{1,3})\\.(\\d{1,3})$",[]))), + <<":1:non-sp1:non-sp2">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[{parts, - 2}]))), - <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), - <<":1:non-sp1:non-sp2">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2 (","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), + 2}]))), + <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), + <<":1:non-sp1:non-sp2">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2 (","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2 (","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[{parts, - 2}]))), - <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2 (","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), + 2}]))), + <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2 (","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), - <<"1IN SOA non-sp1 non-sp2(">> = iolist_to_binary(join(re:split("1IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), + <<"1IN SOA non-sp1 non-sp2(">> = iolist_to_binary(join(re:split("1IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), <<"1IN SOA non-sp1 non-sp2(">> = iolist_to_binary(join(re:split("1IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[{parts, - 2}]))), - <<"1IN SOA non-sp1 non-sp2(">> = iolist_to_binary(join(re:split("1IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), - <<"">> = iolist_to_binary(join(re:split("a.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<"1IN SOA non-sp1 non-sp2(">> = iolist_to_binary(join(re:split("1IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), + <<"">> = iolist_to_binary(join(re:split("a.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<"">> = iolist_to_binary(join(re:split("Z.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<"">> = iolist_to_binary(join(re:split("Z.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("Z.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("Z.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<"">> = iolist_to_binary(join(re:split("2.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("Z.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<"">> = iolist_to_binary(join(re:split("2.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("2.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("2.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<":.pq-r">> = iolist_to_binary(join(re:split("ab-c.pq-r.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("2.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<":.pq-r">> = iolist_to_binary(join(re:split("ab-c.pq-r.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<":.pq-r:">> = iolist_to_binary(join(re:split("ab-c.pq-r.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<":.pq-r:">> = iolist_to_binary(join(re:split("ab-c.pq-r.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<":.uk">> = iolist_to_binary(join(re:split("sxk.zzz.ac.uk.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<":.pq-r:">> = iolist_to_binary(join(re:split("ab-c.pq-r.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<":.uk">> = iolist_to_binary(join(re:split("sxk.zzz.ac.uk.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<":.uk:">> = iolist_to_binary(join(re:split("sxk.zzz.ac.uk.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<":.uk:">> = iolist_to_binary(join(re:split("sxk.zzz.ac.uk.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<":.y-">> = iolist_to_binary(join(re:split("x-.y-.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<":.uk:">> = iolist_to_binary(join(re:split("sxk.zzz.ac.uk.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<":.y-">> = iolist_to_binary(join(re:split("x-.y-.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<":.y-:">> = iolist_to_binary(join(re:split("x-.y-.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<":.y-:">> = iolist_to_binary(join(re:split("x-.y-.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<":.y-:">> = iolist_to_binary(join(re:split("x-.y-.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<"-abc.peq.">> = iolist_to_binary(join(re:split("-abc.peq.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<"-abc.peq.">> = iolist_to_binary(join(re:split("-abc.peq.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[trim]))), <<"-abc.peq.">> = iolist_to_binary(join(re:split("-abc.peq.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[{parts, - 2}]))), - <<"-abc.peq.">> = iolist_to_binary(join(re:split("-abc.peq.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), - <<"">> = iolist_to_binary(join(re:split("*.a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<"-abc.peq.">> = iolist_to_binary(join(re:split("-abc.peq.","^[a-zA-Z\\d][a-zA-Z\\d\\-]*(\\.[a-zA-Z\\d][a-zA-z\\d\\-]*)*\\.$",[]))), + <<"">> = iolist_to_binary(join(re:split("*.a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<"::::">> = iolist_to_binary(join(re:split("*.a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<"::::">> = iolist_to_binary(join(re:split("*.a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<":0-a">> = iolist_to_binary(join(re:split("*.b0-a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<"::::">> = iolist_to_binary(join(re:split("*.a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<":0-a">> = iolist_to_binary(join(re:split("*.b0-a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<":0-a:::">> = iolist_to_binary(join(re:split("*.b0-a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<":0-a:::">> = iolist_to_binary(join(re:split("*.b0-a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<":3-b:.c">> = iolist_to_binary(join(re:split("*.c3-b.c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<":0-a:::">> = iolist_to_binary(join(re:split("*.b0-a","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<":3-b:.c">> = iolist_to_binary(join(re:split("*.c3-b.c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<":3-b:.c::">> = iolist_to_binary(join(re:split("*.c3-b.c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<":3-b:.c::">> = iolist_to_binary(join(re:split("*.c3-b.c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<":-a:.b-c:-c">> = iolist_to_binary(join(re:split("*.c-a.b-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<":3-b:.c::">> = iolist_to_binary(join(re:split("*.c3-b.c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<":-a:.b-c:-c">> = iolist_to_binary(join(re:split("*.c-a.b-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<":-a:.b-c:-c:">> = iolist_to_binary(join(re:split("*.c-a.b-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<":-a:.b-c:-c:">> = iolist_to_binary(join(re:split("*.c-a.b-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<":-a:.b-c:-c:">> = iolist_to_binary(join(re:split("*.c-a.b-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<"*.0">> = iolist_to_binary(join(re:split("*.0","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<"*.0">> = iolist_to_binary(join(re:split("*.0","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<"*.0">> = iolist_to_binary(join(re:split("*.0","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<"*.0">> = iolist_to_binary(join(re:split("*.0","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<"*.a-">> = iolist_to_binary(join(re:split("*.a-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<"*.0">> = iolist_to_binary(join(re:split("*.0","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<"*.a-">> = iolist_to_binary(join(re:split("*.a-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<"*.a-">> = iolist_to_binary(join(re:split("*.a-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<"*.a-">> = iolist_to_binary(join(re:split("*.a-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<"*.a-b.c-">> = iolist_to_binary(join(re:split("*.a-b.c-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<"*.a-">> = iolist_to_binary(join(re:split("*.a-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<"*.a-b.c-">> = iolist_to_binary(join(re:split("*.a-b.c-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<"*.a-b.c-">> = iolist_to_binary(join(re:split("*.a-b.c-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<"*.a-b.c-">> = iolist_to_binary(join(re:split("*.a-b.c-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<"*.c-a.0-c">> = iolist_to_binary(join(re:split("*.c-a.0-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), + 2}]))), + <<"*.a-b.c-">> = iolist_to_binary(join(re:split("*.a-b.c-","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<"*.c-a.0-c">> = iolist_to_binary(join(re:split("*.c-a.0-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[trim]))), <<"*.c-a.0-c">> = iolist_to_binary(join(re:split("*.c-a.0-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[{parts, - 2}]))), - <<"*.c-a.0-c">> = iolist_to_binary(join(re:split("*.c-a.0-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), - <<":de:abd:e">> = iolist_to_binary(join(re:split("abde","^(?=ab(de))(abd)(e)",[trim]))), + 2}]))), + <<"*.c-a.0-c">> = iolist_to_binary(join(re:split("*.c-a.0-c","^\\*\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?(\\.[a-z]([a-z\\-\\d]*[a-z\\d]+)?)*$",[]))), + <<":de:abd:e">> = iolist_to_binary(join(re:split("abde","^(?=ab(de))(abd)(e)",[trim]))), <<":de:abd:e:">> = iolist_to_binary(join(re:split("abde","^(?=ab(de))(abd)(e)",[{parts, - 2}]))), - <<":de:abd:e:">> = iolist_to_binary(join(re:split("abde","^(?=ab(de))(abd)(e)",[]))), - <<"::abd:f">> = iolist_to_binary(join(re:split("abdf","^(?!(ab)de|x)(abd)(f)",[trim]))), + 2}]))), + <<":de:abd:e:">> = iolist_to_binary(join(re:split("abde","^(?=ab(de))(abd)(e)",[]))), + <<"::abd:f">> = iolist_to_binary(join(re:split("abdf","^(?!(ab)de|x)(abd)(f)",[trim]))), <<"::abd:f:">> = iolist_to_binary(join(re:split("abdf","^(?!(ab)de|x)(abd)(f)",[{parts, - 2}]))), - <<"::abd:f:">> = iolist_to_binary(join(re:split("abdf","^(?!(ab)de|x)(abd)(f)",[]))), - <<":abcd:cd:ab:cd">> = iolist_to_binary(join(re:split("abcd","^(?=(ab(cd)))(ab)",[trim]))), + 2}]))), + <<"::abd:f:">> = iolist_to_binary(join(re:split("abdf","^(?!(ab)de|x)(abd)(f)",[]))), + <<":abcd:cd:ab:cd">> = iolist_to_binary(join(re:split("abcd","^(?=(ab(cd)))(ab)",[trim]))), <<":abcd:cd:ab:cd">> = iolist_to_binary(join(re:split("abcd","^(?=(ab(cd)))(ab)",[{parts, - 2}]))), - <<":abcd:cd:ab:cd">> = iolist_to_binary(join(re:split("abcd","^(?=(ab(cd)))(ab)",[]))), + 2}]))), + <<":abcd:cd:ab:cd">> = iolist_to_binary(join(re:split("abcd","^(?=(ab(cd)))(ab)",[]))), <<":.d">> = iolist_to_binary(join(re:split("a.b.c.d","^[\\da-f](\\.[\\da-f])*$",[caseless, - trim]))), + trim]))), <<":.d:">> = iolist_to_binary(join(re:split("a.b.c.d","^[\\da-f](\\.[\\da-f])*$",[caseless, {parts, - 2}]))), - <<":.d:">> = iolist_to_binary(join(re:split("a.b.c.d","^[\\da-f](\\.[\\da-f])*$",[caseless]))), + 2}]))), + <<":.d:">> = iolist_to_binary(join(re:split("a.b.c.d","^[\\da-f](\\.[\\da-f])*$",[caseless]))), <<":.D">> = iolist_to_binary(join(re:split("A.B.C.D","^[\\da-f](\\.[\\da-f])*$",[caseless, - trim]))), + trim]))), <<":.D:">> = iolist_to_binary(join(re:split("A.B.C.D","^[\\da-f](\\.[\\da-f])*$",[caseless, {parts, - 2}]))), - <<":.D:">> = iolist_to_binary(join(re:split("A.B.C.D","^[\\da-f](\\.[\\da-f])*$",[caseless]))), + 2}]))), + <<":.D:">> = iolist_to_binary(join(re:split("A.B.C.D","^[\\da-f](\\.[\\da-f])*$",[caseless]))), <<":.C">> = iolist_to_binary(join(re:split("a.b.c.1.2.3.C","^[\\da-f](\\.[\\da-f])*$",[caseless, - trim]))), + trim]))), <<":.C:">> = iolist_to_binary(join(re:split("a.b.c.1.2.3.C","^[\\da-f](\\.[\\da-f])*$",[caseless, {parts, - 2}]))), - <<":.C:">> = iolist_to_binary(join(re:split("a.b.c.1.2.3.C","^[\\da-f](\\.[\\da-f])*$",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("\"1234\"","^\\\".*\\\"\\s*(;.*)?$",[trim]))), + 2}]))), + <<":.C:">> = iolist_to_binary(join(re:split("a.b.c.1.2.3.C","^[\\da-f](\\.[\\da-f])*$",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("\"1234\"","^\\\".*\\\"\\s*(;.*)?$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("\"1234\"","^\\\".*\\\"\\s*(;.*)?$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("\"1234\"","^\\\".*\\\"\\s*(;.*)?$",[]))), - <<":;">> = iolist_to_binary(join(re:split("\"abcd\" ;","^\\\".*\\\"\\s*(;.*)?$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("\"1234\"","^\\\".*\\\"\\s*(;.*)?$",[]))), + <<":;">> = iolist_to_binary(join(re:split("\"abcd\" ;","^\\\".*\\\"\\s*(;.*)?$",[trim]))), <<":;:">> = iolist_to_binary(join(re:split("\"abcd\" ;","^\\\".*\\\"\\s*(;.*)?$",[{parts, - 2}]))), - <<":;:">> = iolist_to_binary(join(re:split("\"abcd\" ;","^\\\".*\\\"\\s*(;.*)?$",[]))), - <<":; rhubarb">> = iolist_to_binary(join(re:split("\"\" ; rhubarb","^\\\".*\\\"\\s*(;.*)?$",[trim]))), + 2}]))), + <<":;:">> = iolist_to_binary(join(re:split("\"abcd\" ;","^\\\".*\\\"\\s*(;.*)?$",[]))), + <<":; rhubarb">> = iolist_to_binary(join(re:split("\"\" ; rhubarb","^\\\".*\\\"\\s*(;.*)?$",[trim]))), <<":; rhubarb:">> = iolist_to_binary(join(re:split("\"\" ; rhubarb","^\\\".*\\\"\\s*(;.*)?$",[{parts, - 2}]))), - <<":; rhubarb:">> = iolist_to_binary(join(re:split("\"\" ; rhubarb","^\\\".*\\\"\\s*(;.*)?$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\\".*\\\"\\s*(;.*)?$",[trim]))), + 2}]))), + <<":; rhubarb:">> = iolist_to_binary(join(re:split("\"\" ; rhubarb","^\\\".*\\\"\\s*(;.*)?$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\\".*\\\"\\s*(;.*)?$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\\".*\\\"\\s*(;.*)?$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\\".*\\\"\\s*(;.*)?$",[]))), - <<"\"1234\" : things">> = iolist_to_binary(join(re:split("\"1234\" : things","^\\\".*\\\"\\s*(;.*)?$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\\".*\\\"\\s*(;.*)?$",[]))), + <<"\"1234\" : things">> = iolist_to_binary(join(re:split("\"1234\" : things","^\\\".*\\\"\\s*(;.*)?$",[trim]))), <<"\"1234\" : things">> = iolist_to_binary(join(re:split("\"1234\" : things","^\\\".*\\\"\\s*(;.*)?$",[{parts, - 2}]))), - <<"\"1234\" : things">> = iolist_to_binary(join(re:split("\"1234\" : things","^\\\".*\\\"\\s*(;.*)?$",[]))), - <<"">> = iolist_to_binary(join(re:split("","^$",[trim]))), + 2}]))), + <<"\"1234\" : things">> = iolist_to_binary(join(re:split("\"1234\" : things","^\\\".*\\\"\\s*(;.*)?$",[]))), + <<"">> = iolist_to_binary(join(re:split("","^$",[trim]))), <<"">> = iolist_to_binary(join(re:split("","^$",[{parts, - 2}]))), - <<"">> = iolist_to_binary(join(re:split("","^$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^$",[trim]))), + 2}]))), + <<"">> = iolist_to_binary(join(re:split("","^$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^$",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^$",[]))), <<"">> = iolist_to_binary(join(re:split("ab c"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ab c"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab c"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab c"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), <<"abc">> = iolist_to_binary(join(re:split("abc"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, - trim]))), + trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, {parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), <<"ab cde">> = iolist_to_binary(join(re:split("ab cde"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, - trim]))), + trim]))), <<"ab cde">> = iolist_to_binary(join(re:split("ab cde"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended, {parts, - 2}]))), - <<"ab cde">> = iolist_to_binary(join(re:split("ab cde"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), - <<"">> = iolist_to_binary(join(re:split("ab c","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), + 2}]))), + <<"ab cde">> = iolist_to_binary(join(re:split("ab cde"," ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[extended]))), + <<"">> = iolist_to_binary(join(re:split("ab c","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab c","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab c","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab c","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), - <<"ab cde">> = iolist_to_binary(join(re:split("ab cde","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), + <<"ab cde">> = iolist_to_binary(join(re:split("ab cde","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[trim]))), <<"ab cde">> = iolist_to_binary(join(re:split("ab cde","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[{parts, - 2}]))), - <<"ab cde">> = iolist_to_binary(join(re:split("ab cde","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), + 2}]))), + <<"ab cde">> = iolist_to_binary(join(re:split("ab cde","(?x) ^ a (?# begins with a) b\\sc (?# then b c) $ (?# then end)",[]))), <<"">> = iolist_to_binary(join(re:split("a bcd","^ a\\ b[c ]d $",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("a bcd","^ a\\ b[c ]d $",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a bcd","^ a\\ b[c ]d $",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a bcd","^ a\\ b[c ]d $",[extended]))), <<"">> = iolist_to_binary(join(re:split("a b d","^ a\\ b[c ]d $",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("a b d","^ a\\ b[c ]d $",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a b d","^ a\\ b[c ]d $",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a b d","^ a\\ b[c ]d $",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^ a\\ b[c ]d $",[extended, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^ a\\ b[c ]d $",[extended, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^ a\\ b[c ]d $",[extended]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^ a\\ b[c ]d $",[extended]))), <<"abcd">> = iolist_to_binary(join(re:split("abcd","^ a\\ b[c ]d $",[extended, - trim]))), + trim]))), <<"abcd">> = iolist_to_binary(join(re:split("abcd","^ a\\ b[c ]d $",[extended, {parts, - 2}]))), - <<"abcd">> = iolist_to_binary(join(re:split("abcd","^ a\\ b[c ]d $",[extended]))), + 2}]))), + <<"abcd">> = iolist_to_binary(join(re:split("abcd","^ a\\ b[c ]d $",[extended]))), <<"ab d">> = iolist_to_binary(join(re:split("ab d","^ a\\ b[c ]d $",[extended, - trim]))), + trim]))), <<"ab d">> = iolist_to_binary(join(re:split("ab d","^ a\\ b[c ]d $",[extended, {parts, - 2}]))), - <<"ab d">> = iolist_to_binary(join(re:split("ab d","^ a\\ b[c ]d $",[extended]))), - <<":abc:bc:c:def:ef:f:hij:ij:j:klm:lm:m">> = iolist_to_binary(join(re:split("abcdefhijklm","^(a(b(c)))(d(e(f)))(h(i(j)))(k(l(m)))$",[trim]))), + 2}]))), + <<"ab d">> = iolist_to_binary(join(re:split("ab d","^ a\\ b[c ]d $",[extended]))), + <<":abc:bc:c:def:ef:f:hij:ij:j:klm:lm:m">> = iolist_to_binary(join(re:split("abcdefhijklm","^(a(b(c)))(d(e(f)))(h(i(j)))(k(l(m)))$",[trim]))), <<":abc:bc:c:def:ef:f:hij:ij:j:klm:lm:m:">> = iolist_to_binary(join(re:split("abcdefhijklm","^(a(b(c)))(d(e(f)))(h(i(j)))(k(l(m)))$",[{parts, - 2}]))), - <<":abc:bc:c:def:ef:f:hij:ij:j:klm:lm:m:">> = iolist_to_binary(join(re:split("abcdefhijklm","^(a(b(c)))(d(e(f)))(h(i(j)))(k(l(m)))$",[]))), + 2}]))), + <<":abc:bc:c:def:ef:f:hij:ij:j:klm:lm:m:">> = iolist_to_binary(join(re:split("abcdefhijklm","^(a(b(c)))(d(e(f)))(h(i(j)))(k(l(m)))$",[]))), ok. run2() -> - <<":bc:c:ef:f:ij:j:lm:m">> = iolist_to_binary(join(re:split("abcdefhijklm","^(?:a(b(c)))(?:d(e(f)))(?:h(i(j)))(?:k(l(m)))$",[trim]))), + <<":bc:c:ef:f:ij:j:lm:m">> = iolist_to_binary(join(re:split("abcdefhijklm","^(?:a(b(c)))(?:d(e(f)))(?:h(i(j)))(?:k(l(m)))$",[trim]))), <<":bc:c:ef:f:ij:j:lm:m:">> = iolist_to_binary(join(re:split("abcdefhijklm","^(?:a(b(c)))(?:d(e(f)))(?:h(i(j)))(?:k(l(m)))$",[{parts, - 2}]))), - <<":bc:c:ef:f:ij:j:lm:m:">> = iolist_to_binary(join(re:split("abcdefhijklm","^(?:a(b(c)))(?:d(e(f)))(?:h(i(j)))(?:k(l(m)))$",[]))), + 2}]))), + <<":bc:c:ef:f:ij:j:lm:m:">> = iolist_to_binary(join(re:split("abcdefhijklm","^(?:a(b(c)))(?:d(e(f)))(?:h(i(j)))(?:k(l(m)))$",[]))), <<"">> = iolist_to_binary(join(re:split("a+ Z0+ -","^[\\w][\\W][\\s][\\S][\\d][\\D][\\b][\\n][\\c]][\\022]",[trim]))), +","^[\\w][\\W][\\s][\\S][\\d][\\D][\\b][\\n][\\c]][\\022]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a+ Z0+ ","^[\\w][\\W][\\s][\\S][\\d][\\D][\\b][\\n][\\c]][\\022]",[{parts, - 2}]))), + 2}]))), <<":">> = iolist_to_binary(join(re:split("a+ Z0+ -","^[\\w][\\W][\\s][\\S][\\d][\\D][\\b][\\n][\\c]][\\022]",[]))), - <<"">> = iolist_to_binary(join(re:split(".^$(*+)|{?,?}","^[.^$|()*+?{,}]+",[trim]))), +","^[\\w][\\W][\\s][\\S][\\d][\\D][\\b][\\n][\\c]][\\022]",[]))), + <<"">> = iolist_to_binary(join(re:split(".^$(*+)|{?,?}","^[.^$|()*+?{,}]+",[trim]))), <<":">> = iolist_to_binary(join(re:split(".^$(*+)|{?,?}","^[.^$|()*+?{,}]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split(".^$(*+)|{?,?}","^[.^$|()*+?{,}]+",[]))), - <<"">> = iolist_to_binary(join(re:split("z","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split(".^$(*+)|{?,?}","^[.^$|()*+?{,}]+",[]))), + <<"">> = iolist_to_binary(join(re:split("z","^a*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("z","^a*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("z","^a*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("az","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("z","^a*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("az","^a*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("az","^a*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("az","^a*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaz","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("az","^a*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaz","^a*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaz","^a*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaz","^a*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaz","^a*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^a*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","^a*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","^a*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aa","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","^a*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aa","^a*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aa","^a*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aa","^a*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aa","^a*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","^a*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w",[]))), - <<":+">> = iolist_to_binary(join(re:split("a+","^a*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w",[]))), + <<":+">> = iolist_to_binary(join(re:split("a+","^a*\\w",[trim]))), <<":+">> = iolist_to_binary(join(re:split("a+","^a*\\w",[{parts, - 2}]))), - <<":+">> = iolist_to_binary(join(re:split("a+","^a*\\w",[]))), - <<":+">> = iolist_to_binary(join(re:split("aa+","^a*\\w",[trim]))), + 2}]))), + <<":+">> = iolist_to_binary(join(re:split("a+","^a*\\w",[]))), + <<":+">> = iolist_to_binary(join(re:split("aa+","^a*\\w",[trim]))), <<":+">> = iolist_to_binary(join(re:split("aa+","^a*\\w",[{parts, - 2}]))), - <<":+">> = iolist_to_binary(join(re:split("aa+","^a*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("z","^a*?\\w",[trim]))), + 2}]))), + <<":+">> = iolist_to_binary(join(re:split("aa+","^a*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("z","^a*?\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("z","^a*?\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("z","^a*?\\w",[]))), - <<":z">> = iolist_to_binary(join(re:split("az","^a*?\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("z","^a*?\\w",[]))), + <<":z">> = iolist_to_binary(join(re:split("az","^a*?\\w",[trim]))), <<":z">> = iolist_to_binary(join(re:split("az","^a*?\\w",[{parts, - 2}]))), - <<":z">> = iolist_to_binary(join(re:split("az","^a*?\\w",[]))), - <<":aaz">> = iolist_to_binary(join(re:split("aaaz","^a*?\\w",[trim]))), + 2}]))), + <<":z">> = iolist_to_binary(join(re:split("az","^a*?\\w",[]))), + <<":aaz">> = iolist_to_binary(join(re:split("aaaz","^a*?\\w",[trim]))), <<":aaz">> = iolist_to_binary(join(re:split("aaaz","^a*?\\w",[{parts, - 2}]))), - <<":aaz">> = iolist_to_binary(join(re:split("aaaz","^a*?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^a*?\\w",[trim]))), + 2}]))), + <<":aaz">> = iolist_to_binary(join(re:split("aaaz","^a*?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^a*?\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","^a*?\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","^a*?\\w",[]))), - <<":a">> = iolist_to_binary(join(re:split("aa","^a*?\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","^a*?\\w",[]))), + <<":a">> = iolist_to_binary(join(re:split("aa","^a*?\\w",[trim]))), <<":a">> = iolist_to_binary(join(re:split("aa","^a*?\\w",[{parts, - 2}]))), - <<":a">> = iolist_to_binary(join(re:split("aa","^a*?\\w",[]))), - <<":aaa">> = iolist_to_binary(join(re:split("aaaa","^a*?\\w",[trim]))), + 2}]))), + <<":a">> = iolist_to_binary(join(re:split("aa","^a*?\\w",[]))), + <<":aaa">> = iolist_to_binary(join(re:split("aaaa","^a*?\\w",[trim]))), <<":aaa">> = iolist_to_binary(join(re:split("aaaa","^a*?\\w",[{parts, - 2}]))), - <<":aaa">> = iolist_to_binary(join(re:split("aaaa","^a*?\\w",[]))), - <<":+">> = iolist_to_binary(join(re:split("a+","^a*?\\w",[trim]))), + 2}]))), + <<":aaa">> = iolist_to_binary(join(re:split("aaaa","^a*?\\w",[]))), + <<":+">> = iolist_to_binary(join(re:split("a+","^a*?\\w",[trim]))), <<":+">> = iolist_to_binary(join(re:split("a+","^a*?\\w",[{parts, - 2}]))), - <<":+">> = iolist_to_binary(join(re:split("a+","^a*?\\w",[]))), - <<":a+">> = iolist_to_binary(join(re:split("aa+","^a*?\\w",[trim]))), + 2}]))), + <<":+">> = iolist_to_binary(join(re:split("a+","^a*?\\w",[]))), + <<":a+">> = iolist_to_binary(join(re:split("aa+","^a*?\\w",[trim]))), <<":a+">> = iolist_to_binary(join(re:split("aa+","^a*?\\w",[{parts, - 2}]))), - <<":a+">> = iolist_to_binary(join(re:split("aa+","^a*?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("az","^a+\\w",[trim]))), + 2}]))), + <<":a+">> = iolist_to_binary(join(re:split("aa+","^a*?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("az","^a+\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("az","^a+\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("az","^a+\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaz","^a+\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("az","^a+\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaz","^a+\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaz","^a+\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaz","^a+\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aa","^a+\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaz","^a+\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aa","^a+\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aa","^a+\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aa","^a+\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","^a+\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aa","^a+\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","^a+\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^a+\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^a+\\w",[]))), - <<":+">> = iolist_to_binary(join(re:split("aa+","^a+\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^a+\\w",[]))), + <<":+">> = iolist_to_binary(join(re:split("aa+","^a+\\w",[trim]))), <<":+">> = iolist_to_binary(join(re:split("aa+","^a+\\w",[{parts, - 2}]))), - <<":+">> = iolist_to_binary(join(re:split("aa+","^a+\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("az","^a+?\\w",[trim]))), + 2}]))), + <<":+">> = iolist_to_binary(join(re:split("aa+","^a+\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("az","^a+?\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("az","^a+?\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("az","^a+?\\w",[]))), - <<":az">> = iolist_to_binary(join(re:split("aaaz","^a+?\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("az","^a+?\\w",[]))), + <<":az">> = iolist_to_binary(join(re:split("aaaz","^a+?\\w",[trim]))), <<":az">> = iolist_to_binary(join(re:split("aaaz","^a+?\\w",[{parts, - 2}]))), - <<":az">> = iolist_to_binary(join(re:split("aaaz","^a+?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aa","^a+?\\w",[trim]))), + 2}]))), + <<":az">> = iolist_to_binary(join(re:split("aaaz","^a+?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aa","^a+?\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aa","^a+?\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aa","^a+?\\w",[]))), - <<":aa">> = iolist_to_binary(join(re:split("aaaa","^a+?\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aa","^a+?\\w",[]))), + <<":aa">> = iolist_to_binary(join(re:split("aaaa","^a+?\\w",[trim]))), <<":aa">> = iolist_to_binary(join(re:split("aaaa","^a+?\\w",[{parts, - 2}]))), - <<":aa">> = iolist_to_binary(join(re:split("aaaa","^a+?\\w",[]))), - <<":+">> = iolist_to_binary(join(re:split("aa+","^a+?\\w",[trim]))), + 2}]))), + <<":aa">> = iolist_to_binary(join(re:split("aaaa","^a+?\\w",[]))), + <<":+">> = iolist_to_binary(join(re:split("aa+","^a+?\\w",[trim]))), <<":+">> = iolist_to_binary(join(re:split("aa+","^a+?\\w",[{parts, - 2}]))), - <<":+">> = iolist_to_binary(join(re:split("aa+","^a+?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("1234567890","^\\d{8}\\w{2,}",[trim]))), + 2}]))), + <<":+">> = iolist_to_binary(join(re:split("aa+","^a+?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("1234567890","^\\d{8}\\w{2,}",[trim]))), <<":">> = iolist_to_binary(join(re:split("1234567890","^\\d{8}\\w{2,}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1234567890","^\\d{8}\\w{2,}",[]))), - <<"">> = iolist_to_binary(join(re:split("12345678ab","^\\d{8}\\w{2,}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1234567890","^\\d{8}\\w{2,}",[]))), + <<"">> = iolist_to_binary(join(re:split("12345678ab","^\\d{8}\\w{2,}",[trim]))), <<":">> = iolist_to_binary(join(re:split("12345678ab","^\\d{8}\\w{2,}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12345678ab","^\\d{8}\\w{2,}",[]))), - <<"">> = iolist_to_binary(join(re:split("12345678__","^\\d{8}\\w{2,}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12345678ab","^\\d{8}\\w{2,}",[]))), + <<"">> = iolist_to_binary(join(re:split("12345678__","^\\d{8}\\w{2,}",[trim]))), <<":">> = iolist_to_binary(join(re:split("12345678__","^\\d{8}\\w{2,}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12345678__","^\\d{8}\\w{2,}",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8}\\w{2,}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12345678__","^\\d{8}\\w{2,}",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8}\\w{2,}",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8}\\w{2,}",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8}\\w{2,}",[]))), - <<"1234567">> = iolist_to_binary(join(re:split("1234567","^\\d{8}\\w{2,}",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8}\\w{2,}",[]))), + <<"1234567">> = iolist_to_binary(join(re:split("1234567","^\\d{8}\\w{2,}",[trim]))), <<"1234567">> = iolist_to_binary(join(re:split("1234567","^\\d{8}\\w{2,}",[{parts, - 2}]))), - <<"1234567">> = iolist_to_binary(join(re:split("1234567","^\\d{8}\\w{2,}",[]))), - <<"">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}$",[trim]))), + 2}]))), + <<"1234567">> = iolist_to_binary(join(re:split("1234567","^\\d{8}\\w{2,}",[]))), + <<"">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}$",[trim]))), <<":">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}$",[]))), - <<"">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}$",[]))), + <<"">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}$",[trim]))), <<":">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}$",[]))), - <<"">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}$",[]))), + <<"">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}$",[trim]))), <<":">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}$",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}$",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}$",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[aeiou\\d]{4,5}$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[aeiou\\d]{4,5}$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[aeiou\\d]{4,5}$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[aeiou\\d]{4,5}$",[]))), - <<"123456">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[aeiou\\d]{4,5}$",[]))), + <<"123456">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}$",[trim]))), <<"123456">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}$",[{parts, - 2}]))), - <<"123456">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}$",[]))), - <<"">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}?",[trim]))), + 2}]))), + <<"123456">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}$",[]))), + <<"">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}?",[trim]))), <<":">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}?",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}?",[]))), - <<"">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}?",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("uoie","^[aeiou\\d]{4,5}?",[]))), + <<"">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}?",[trim]))), <<":">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}?",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}?",[]))), - <<":5">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}?",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1234","^[aeiou\\d]{4,5}?",[]))), + <<":5">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}?",[trim]))), <<":5">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}?",[{parts, - 2}]))), - <<":5">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}?",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}?",[trim]))), + 2}]))), + <<":5">> = iolist_to_binary(join(re:split("12345","^[aeiou\\d]{4,5}?",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}?",[trim]))), <<":a">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}?",[{parts, - 2}]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}?",[]))), - <<":56">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}?",[trim]))), + 2}]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaa","^[aeiou\\d]{4,5}?",[]))), + <<":56">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}?",[trim]))), <<":56">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}?",[{parts, - 2}]))), - <<":56">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}?",[]))), - <<":abc:abc">> = iolist_to_binary(join(re:split("abc=abcabc","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), + 2}]))), + <<":56">> = iolist_to_binary(join(re:split("123456","^[aeiou\\d]{4,5}?",[]))), + <<":abc:abc">> = iolist_to_binary(join(re:split("abc=abcabc","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), <<":abc:abc:">> = iolist_to_binary(join(re:split("abc=abcabc","\\A(abc|def)=(\\1){2,3}\\Z",[{parts, - 2}]))), - <<":abc:abc:">> = iolist_to_binary(join(re:split("abc=abcabc","\\A(abc|def)=(\\1){2,3}\\Z",[]))), - <<":def:def">> = iolist_to_binary(join(re:split("def=defdefdef","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), + 2}]))), + <<":abc:abc:">> = iolist_to_binary(join(re:split("abc=abcabc","\\A(abc|def)=(\\1){2,3}\\Z",[]))), + <<":def:def">> = iolist_to_binary(join(re:split("def=defdefdef","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), <<":def:def:">> = iolist_to_binary(join(re:split("def=defdefdef","\\A(abc|def)=(\\1){2,3}\\Z",[{parts, - 2}]))), - <<":def:def:">> = iolist_to_binary(join(re:split("def=defdefdef","\\A(abc|def)=(\\1){2,3}\\Z",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), + 2}]))), + <<":def:def:">> = iolist_to_binary(join(re:split("def=defdefdef","\\A(abc|def)=(\\1){2,3}\\Z",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\A(abc|def)=(\\1){2,3}\\Z",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\A(abc|def)=(\\1){2,3}\\Z",[]))), - <<"abc=defdef">> = iolist_to_binary(join(re:split("abc=defdef","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\A(abc|def)=(\\1){2,3}\\Z",[]))), + <<"abc=defdef">> = iolist_to_binary(join(re:split("abc=defdef","\\A(abc|def)=(\\1){2,3}\\Z",[trim]))), <<"abc=defdef">> = iolist_to_binary(join(re:split("abc=defdef","\\A(abc|def)=(\\1){2,3}\\Z",[{parts, - 2}]))), - <<"abc=defdef">> = iolist_to_binary(join(re:split("abc=defdef","\\A(abc|def)=(\\1){2,3}\\Z",[]))), - <<":a:b:c:d:e:f:g:h:i:j:k:cd">> = iolist_to_binary(join(re:split("abcdefghijkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[trim]))), + 2}]))), + <<"abc=defdef">> = iolist_to_binary(join(re:split("abc=defdef","\\A(abc|def)=(\\1){2,3}\\Z",[]))), + <<":a:b:c:d:e:f:g:h:i:j:k:cd">> = iolist_to_binary(join(re:split("abcdefghijkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[trim]))), <<":a:b:c:d:e:f:g:h:i:j:k:cd:">> = iolist_to_binary(join(re:split("abcdefghijkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[{parts, - 2}]))), - <<":a:b:c:d:e:f:g:h:i:j:k:cd:">> = iolist_to_binary(join(re:split("abcdefghijkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[]))), - <<":a:b:c:d:e:f:g:h:i:j:k:cd">> = iolist_to_binary(join(re:split("abcdefghijkkkkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[trim]))), + 2}]))), + <<":a:b:c:d:e:f:g:h:i:j:k:cd:">> = iolist_to_binary(join(re:split("abcdefghijkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[]))), + <<":a:b:c:d:e:f:g:h:i:j:k:cd">> = iolist_to_binary(join(re:split("abcdefghijkkkkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[trim]))), <<":a:b:c:d:e:f:g:h:i:j:k:cd:">> = iolist_to_binary(join(re:split("abcdefghijkkkkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[{parts, - 2}]))), - <<":a:b:c:d:e:f:g:h:i:j:k:cd:">> = iolist_to_binary(join(re:split("abcdefghijkkkkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[]))), - <<":cataract:aract:ract::3">> = iolist_to_binary(join(re:split("cataract cataract23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[trim]))), + 2}]))), + <<":a:b:c:d:e:f:g:h:i:j:k:cd:">> = iolist_to_binary(join(re:split("abcdefghijkkkkcda2","^(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\11*(\\3\\4)\\1(?#)2$",[]))), + <<":cataract:aract:ract::3">> = iolist_to_binary(join(re:split("cataract cataract23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[trim]))), <<":cataract:aract:ract::3:">> = iolist_to_binary(join(re:split("cataract cataract23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[{parts, - 2}]))), - <<":cataract:aract:ract::3:">> = iolist_to_binary(join(re:split("cataract cataract23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[]))), - <<":catatonic:atonic:tonic::3">> = iolist_to_binary(join(re:split("catatonic catatonic23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[trim]))), + 2}]))), + <<":cataract:aract:ract::3:">> = iolist_to_binary(join(re:split("cataract cataract23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[]))), + <<":catatonic:atonic:tonic::3">> = iolist_to_binary(join(re:split("catatonic catatonic23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[trim]))), <<":catatonic:atonic:tonic::3:">> = iolist_to_binary(join(re:split("catatonic catatonic23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[{parts, - 2}]))), - <<":catatonic:atonic:tonic::3:">> = iolist_to_binary(join(re:split("catatonic catatonic23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[]))), - <<":caterpillar:erpillar:::3">> = iolist_to_binary(join(re:split("caterpillar caterpillar23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[trim]))), + 2}]))), + <<":catatonic:atonic:tonic::3:">> = iolist_to_binary(join(re:split("catatonic catatonic23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[]))), + <<":caterpillar:erpillar:::3">> = iolist_to_binary(join(re:split("caterpillar caterpillar23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[trim]))), <<":caterpillar:erpillar:::3:">> = iolist_to_binary(join(re:split("caterpillar caterpillar23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[{parts, - 2}]))), - <<":caterpillar:erpillar:::3:">> = iolist_to_binary(join(re:split("caterpillar caterpillar23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[]))), - <<":abcd::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From +([^ ]+) +[a-zA-Z][a-zA-Z][a-zA-Z] +[a-zA-Z][a-zA-Z][a-zA-Z] +[0-9]?[0-9] +[0-9][0-9]:[0-9][0-9]",[trim]))), + 2}]))), + <<":caterpillar:erpillar:::3:">> = iolist_to_binary(join(re:split("caterpillar caterpillar23","(cat(a(ract|tonic)|erpillar)) \\1()2(3)",[]))), + <<":abcd::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From +([^ ]+) +[a-zA-Z][a-zA-Z][a-zA-Z] +[a-zA-Z][a-zA-Z][a-zA-Z] +[0-9]?[0-9] +[0-9][0-9]:[0-9][0-9]",[trim]))), <<":abcd::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From +([^ ]+) +[a-zA-Z][a-zA-Z][a-zA-Z] +[a-zA-Z][a-zA-Z][a-zA-Z] +[0-9]?[0-9] +[0-9][0-9]:[0-9][0-9]",[{parts, - 2}]))), - <<":abcd::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From +([^ ]+) +[a-zA-Z][a-zA-Z][a-zA-Z] +[a-zA-Z][a-zA-Z][a-zA-Z] +[0-9]?[0-9] +[0-9][0-9]:[0-9][0-9]",[]))), - <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), + 2}]))), + <<":abcd::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From +([^ ]+) +[a-zA-Z][a-zA-Z][a-zA-Z] +[a-zA-Z][a-zA-Z][a-zA-Z] +[0-9]?[0-9] +[0-9][0-9]:[0-9][0-9]",[]))), + <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[{parts, - 2}]))), - <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), - <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 1 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), + 2}]))), + <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), + <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 1 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 1 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[{parts, - 2}]))), - <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 1 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), + 2}]))), + <<":Sep ::02 1997">> = iolist_to_binary(join(re:split("From abcd Mon Sep 1 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), - <<"From abcd Sep 01 12:33:02 1997">> = iolist_to_binary(join(re:split("From abcd Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), + <<"From abcd Sep 01 12:33:02 1997">> = iolist_to_binary(join(re:split("From abcd Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[trim]))), <<"From abcd Sep 01 12:33:02 1997">> = iolist_to_binary(join(re:split("From abcd Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[{parts, - 2}]))), - <<"From abcd Sep 01 12:33:02 1997">> = iolist_to_binary(join(re:split("From abcd Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), + 2}]))), + <<"From abcd Sep 01 12:33:02 1997">> = iolist_to_binary(join(re:split("From abcd Sep 01 12:33:02 1997","^From\\s+\\S+\\s+([a-zA-Z]{3}\\s+){2}\\d{1,2}\\s+\\d\\d:\\d\\d",[]))), <<"">> = iolist_to_binary(join(re:split("12 -34","^12.34",[dotall,trim]))), +34","^12.34",[dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("12 -34","^12.34",[dotall,{parts,2}]))), +34","^12.34",[dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("12 -34","^12.34",[dotall]))), +34","^12.34",[dotall]))), <<"">> = iolist_to_binary(join(re:split("12
34","^12.34",[dotall, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("12
34","^12.34",[dotall, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12
34","^12.34",[dotall]))), - <<"the quick : fox">> = iolist_to_binary(join(re:split("the quick brown fox","\\w+(?=\\t)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12
34","^12.34",[dotall]))), + <<"the quick : fox">> = iolist_to_binary(join(re:split("the quick brown fox","\\w+(?=\\t)",[trim]))), <<"the quick : fox">> = iolist_to_binary(join(re:split("the quick brown fox","\\w+(?=\\t)",[{parts, - 2}]))), - <<"the quick : fox">> = iolist_to_binary(join(re:split("the quick brown fox","\\w+(?=\\t)",[]))), - <<"foobar is :lish see?">> = iolist_to_binary(join(re:split("foobar is foolish see?","foo(?!bar)(.*)",[trim]))), + 2}]))), + <<"the quick : fox">> = iolist_to_binary(join(re:split("the quick brown fox","\\w+(?=\\t)",[]))), + <<"foobar is :lish see?">> = iolist_to_binary(join(re:split("foobar is foolish see?","foo(?!bar)(.*)",[trim]))), <<"foobar is :lish see?:">> = iolist_to_binary(join(re:split("foobar is foolish see?","foo(?!bar)(.*)",[{parts, - 2}]))), - <<"foobar is :lish see?:">> = iolist_to_binary(join(re:split("foobar is foolish see?","foo(?!bar)(.*)",[]))), - <<"foobar c: etc">> = iolist_to_binary(join(re:split("foobar crowbar etc","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), + 2}]))), + <<"foobar is :lish see?:">> = iolist_to_binary(join(re:split("foobar is foolish see?","foo(?!bar)(.*)",[]))), + <<"foobar c: etc">> = iolist_to_binary(join(re:split("foobar crowbar etc","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), <<"foobar c: etc:">> = iolist_to_binary(join(re:split("foobar crowbar etc","(?:(?!foo)...|^.{0,2})bar(.*)",[{parts, - 2}]))), - <<"foobar c: etc:">> = iolist_to_binary(join(re:split("foobar crowbar etc","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), - <<":rel">> = iolist_to_binary(join(re:split("barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), + 2}]))), + <<"foobar c: etc:">> = iolist_to_binary(join(re:split("foobar crowbar etc","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), + <<":rel">> = iolist_to_binary(join(re:split("barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), <<":rel:">> = iolist_to_binary(join(re:split("barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[{parts, - 2}]))), - <<":rel:">> = iolist_to_binary(join(re:split("barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), - <<":rel">> = iolist_to_binary(join(re:split("2barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), + 2}]))), + <<":rel:">> = iolist_to_binary(join(re:split("barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), + <<":rel">> = iolist_to_binary(join(re:split("2barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), <<":rel:">> = iolist_to_binary(join(re:split("2barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[{parts, - 2}]))), - <<":rel:">> = iolist_to_binary(join(re:split("2barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), - <<":rel">> = iolist_to_binary(join(re:split("A barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), + 2}]))), + <<":rel:">> = iolist_to_binary(join(re:split("2barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), + <<":rel">> = iolist_to_binary(join(re:split("A barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[trim]))), <<":rel:">> = iolist_to_binary(join(re:split("A barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[{parts, - 2}]))), - <<":rel:">> = iolist_to_binary(join(re:split("A barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), - <<":abc:456">> = iolist_to_binary(join(re:split("abc456","^(\\D*)(?=\\d)(?!123)",[trim]))), + 2}]))), + <<":rel:">> = iolist_to_binary(join(re:split("A barrel","(?:(?!foo)...|^.{0,2})bar(.*)",[]))), + <<":abc:456">> = iolist_to_binary(join(re:split("abc456","^(\\D*)(?=\\d)(?!123)",[trim]))), <<":abc:456">> = iolist_to_binary(join(re:split("abc456","^(\\D*)(?=\\d)(?!123)",[{parts, - 2}]))), - <<":abc:456">> = iolist_to_binary(join(re:split("abc456","^(\\D*)(?=\\d)(?!123)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[trim]))), + 2}]))), + <<":abc:456">> = iolist_to_binary(join(re:split("abc456","^(\\D*)(?=\\d)(?!123)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[]))), - <<"abc123">> = iolist_to_binary(join(re:split("abc123","^(\\D*)(?=\\d)(?!123)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[]))), + <<"abc123">> = iolist_to_binary(join(re:split("abc123","^(\\D*)(?=\\d)(?!123)",[trim]))), <<"abc123">> = iolist_to_binary(join(re:split("abc123","^(\\D*)(?=\\d)(?!123)",[{parts, - 2}]))), - <<"abc123">> = iolist_to_binary(join(re:split("abc123","^(\\D*)(?=\\d)(?!123)",[]))), + 2}]))), + <<"abc123">> = iolist_to_binary(join(re:split("abc123","^(\\D*)(?=\\d)(?!123)",[]))), ok. run3() -> <<"">> = iolist_to_binary(join(re:split("1234","^1234(?# test newlines - inside)",[trim]))), + inside)",[trim]))), <<":">> = iolist_to_binary(join(re:split("1234","^1234(?# test newlines - inside)",[{parts,2}]))), + inside)",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("1234","^1234(?# test newlines - inside)",[]))), + inside)",[]))), <<"">> = iolist_to_binary(join(re:split("1234","^1234 #comment in extended re - ",[extended,trim]))), + ",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("1234","^1234 #comment in extended re - ",[extended,{parts,2}]))), + ",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("1234","^1234 #comment in extended re - ",[extended]))), + ",[extended]))), <<"">> = iolist_to_binary(join(re:split("abcd","#rhubarb - abcd",[extended,trim]))), + abcd",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("abcd","#rhubarb - abcd",[extended,{parts,2}]))), + abcd",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abcd","#rhubarb - abcd",[extended]))), + abcd",[extended]))), <<"">> = iolist_to_binary(join(re:split("abcd","^abcd#rhubarb",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abcd","^abcd#rhubarb",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcd","^abcd#rhubarb",[extended]))), - <<":a:b">> = iolist_to_binary(join(re:split("aaab","^(a)\\1{2,3}(.)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcd","^abcd#rhubarb",[extended]))), + <<":a:b">> = iolist_to_binary(join(re:split("aaab","^(a)\\1{2,3}(.)",[trim]))), <<":a:b:">> = iolist_to_binary(join(re:split("aaab","^(a)\\1{2,3}(.)",[{parts, - 2}]))), - <<":a:b:">> = iolist_to_binary(join(re:split("aaab","^(a)\\1{2,3}(.)",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aaaab","^(a)\\1{2,3}(.)",[trim]))), + 2}]))), + <<":a:b:">> = iolist_to_binary(join(re:split("aaab","^(a)\\1{2,3}(.)",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aaaab","^(a)\\1{2,3}(.)",[trim]))), <<":a:b:">> = iolist_to_binary(join(re:split("aaaab","^(a)\\1{2,3}(.)",[{parts, - 2}]))), - <<":a:b:">> = iolist_to_binary(join(re:split("aaaab","^(a)\\1{2,3}(.)",[]))), - <<":a:a:b">> = iolist_to_binary(join(re:split("aaaaab","^(a)\\1{2,3}(.)",[trim]))), + 2}]))), + <<":a:b:">> = iolist_to_binary(join(re:split("aaaab","^(a)\\1{2,3}(.)",[]))), + <<":a:a:b">> = iolist_to_binary(join(re:split("aaaaab","^(a)\\1{2,3}(.)",[trim]))), <<":a:a:b">> = iolist_to_binary(join(re:split("aaaaab","^(a)\\1{2,3}(.)",[{parts, - 2}]))), - <<":a:a:b">> = iolist_to_binary(join(re:split("aaaaab","^(a)\\1{2,3}(.)",[]))), - <<":a:a:ab">> = iolist_to_binary(join(re:split("aaaaaab","^(a)\\1{2,3}(.)",[trim]))), + 2}]))), + <<":a:a:b">> = iolist_to_binary(join(re:split("aaaaab","^(a)\\1{2,3}(.)",[]))), + <<":a:a:ab">> = iolist_to_binary(join(re:split("aaaaaab","^(a)\\1{2,3}(.)",[trim]))), <<":a:a:ab">> = iolist_to_binary(join(re:split("aaaaaab","^(a)\\1{2,3}(.)",[{parts, - 2}]))), - <<":a:a:ab">> = iolist_to_binary(join(re:split("aaaaaab","^(a)\\1{2,3}(.)",[]))), - <<"the ">> = iolist_to_binary(join(re:split("the abc","(?!^)abc",[trim]))), + 2}]))), + <<":a:a:ab">> = iolist_to_binary(join(re:split("aaaaaab","^(a)\\1{2,3}(.)",[]))), + <<"the ">> = iolist_to_binary(join(re:split("the abc","(?!^)abc",[trim]))), <<"the :">> = iolist_to_binary(join(re:split("the abc","(?!^)abc",[{parts, - 2}]))), - <<"the :">> = iolist_to_binary(join(re:split("the abc","(?!^)abc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?!^)abc",[trim]))), + 2}]))), + <<"the :">> = iolist_to_binary(join(re:split("the abc","(?!^)abc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?!^)abc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?!^)abc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?!^)abc",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(?!^)abc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?!^)abc",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(?!^)abc",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(?!^)abc",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(?!^)abc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","(?=^)abc",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(?!^)abc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","(?=^)abc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","(?=^)abc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","(?=^)abc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=^)abc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","(?=^)abc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=^)abc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=^)abc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=^)abc",[]))), - <<"the abc">> = iolist_to_binary(join(re:split("the abc","(?=^)abc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=^)abc",[]))), + <<"the abc">> = iolist_to_binary(join(re:split("the abc","(?=^)abc",[trim]))), <<"the abc">> = iolist_to_binary(join(re:split("the abc","(?=^)abc",[{parts, - 2}]))), - <<"the abc">> = iolist_to_binary(join(re:split("the abc","(?=^)abc",[]))), - <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*|b)",[trim]))), + 2}]))), + <<"the abc">> = iolist_to_binary(join(re:split("the abc","(?=^)abc",[]))), + <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*|b)",[trim]))), <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*|b)",[{parts, - 2}]))), - <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*|b)",[]))), - <<":abbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*|b)",[trim]))), + 2}]))), + <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*|b)",[]))), + <<":abbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*|b)",[trim]))), <<":abbbbb:">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*|b)",[{parts, - 2}]))), - <<":abbbbb:">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*|b)",[]))), - <<":a:bbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*?|b)",[trim]))), + 2}]))), + <<":abbbbb:">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*|b)",[]))), + <<":a:bbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*?|b)",[trim]))), <<":a:bbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*?|b)",[{parts, - 2}]))), - <<":a:bbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*?|b)",[]))), - <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*?|b)",[trim]))), + 2}]))), + <<":a:bbbbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}?(ab*?|b)",[]))), + <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*?|b)",[trim]))), <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*?|b)",[{parts, - 2}]))), - <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*?|b)",[]))), + 2}]))), + <<":b:bbb">> = iolist_to_binary(join(re:split("aabbbbb","^[ab]{1,3}(ab*?|b)",[]))), <<"Alan Other <user.ain>">> = iolist_to_binary(join(re:split("Alan Other <user.ain>"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -1630,7 +1630,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"Alan Other <user.ain>">> = iolist_to_binary(join(re:split("Alan Other <user.ain>"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -1824,7 +1824,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"Alan Other <user.ain>">> = iolist_to_binary(join(re:split("Alan Other <user.ain>"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -2017,7 +2017,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"<user.ain>">> = iolist_to_binary(join(re:split("<user.ain>"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -2210,7 +2210,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"<user.ain>">> = iolist_to_binary(join(re:split("<user.ain>"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -2404,7 +2404,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"<user.ain>">> = iolist_to_binary(join(re:split("<user.ain>"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -2597,7 +2597,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"user.ain">> = iolist_to_binary(join(re:split("user.ain"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -2790,7 +2790,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"user.ain">> = iolist_to_binary(join(re:split("user.ain"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -2984,7 +2984,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"user.ain">> = iolist_to_binary(join(re:split("user.ain"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -3177,7 +3177,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"\"A. Other\" <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("\"A. Other\" <user.1234.ain> (a comment)"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -3370,7 +3370,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"\"A. Other\" <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("\"A. Other\" <user.1234.ain> (a comment)"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -3564,7 +3564,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"\"A. Other\" <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("\"A. Other\" <user.1234.ain> (a comment)"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -3757,7 +3757,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"A. Other <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("A. Other <user.1234.ain> (a comment)"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -3950,7 +3950,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"A. Other <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("A. Other <user.1234.ain> (a comment)"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -4144,7 +4144,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"A. Other <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("A. Other <user.1234.ain> (a comment)"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -4337,7 +4337,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay">> = iolist_to_binary(join(re:split("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -4530,7 +4530,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay">> = iolist_to_binary(join(re:split("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -4724,7 +4724,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay">> = iolist_to_binary(join(re:split("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -4917,7 +4917,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"A missing angle <user.where">> = iolist_to_binary(join(re:split("A missing angle <user.where"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -5110,7 +5110,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"A missing angle <user.where">> = iolist_to_binary(join(re:split("A missing angle <user.where"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -5304,7 +5304,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"A missing angle <user.where">> = iolist_to_binary(join(re:split("A missing angle <user.where"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -5497,7 +5497,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -5690,7 +5690,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -5884,7 +5884,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -6077,7 +6077,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -6270,7 +6270,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended,trim]))), +\\) )* # optional trailing comment",[extended,trim]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -6464,7 +6464,7 @@ run3() -> ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional trailing comment",[extended, - {parts,2}]))), + {parts,2}]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox"," (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* \\) )* # optional leading comment @@ -6657,7 +6657,7 @@ run3() -> # name and address ) (?: [\\040\\t] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] | \\( (?: [^\\\\\\x80-\\xff\\n\\015()] | \\\\ [^\\x80-\\xff] )* \\) )* -\\) )* # optional trailing comment",[extended]))), +\\) )* # optional trailing comment",[extended]))), <<"Alan Other <user.ain>">> = iolist_to_binary(join(re:split("Alan Other <user.ain>","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -7238,7 +7238,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"Alan Other <user.ain>">> = iolist_to_binary(join(re:split("Alan Other <user.ain>","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -7819,7 +7819,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"Alan Other <user.ain>">> = iolist_to_binary(join(re:split("Alan Other <user.ain>","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -8400,7 +8400,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"<user.ain>">> = iolist_to_binary(join(re:split("<user.ain>","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -8981,7 +8981,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"<user.ain>">> = iolist_to_binary(join(re:split("<user.ain>","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -9562,7 +9562,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"<user.ain>">> = iolist_to_binary(join(re:split("<user.ain>","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -10143,7 +10143,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"user.ain">> = iolist_to_binary(join(re:split("user.ain","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -10724,7 +10724,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"user.ain">> = iolist_to_binary(join(re:split("user.ain","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -11305,7 +11305,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"user.ain">> = iolist_to_binary(join(re:split("user.ain","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -11886,7 +11886,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"\"A. Other\" <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("\"A. Other\" <user.1234.ain> (a comment)","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -12467,7 +12467,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"\"A. Other\" <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("\"A. Other\" <user.1234.ain> (a comment)","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -13048,7 +13048,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"\"A. Other\" <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("\"A. Other\" <user.1234.ain> (a comment)","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -13629,7 +13629,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"A. Other <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("A. Other <user.1234.ain> (a comment)","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -14210,7 +14210,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"A. Other <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("A. Other <user.1234.ain> (a comment)","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -14791,7 +14791,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"A. Other <user.1234.ain> (a comment)">> = iolist_to_binary(join(re:split("A. Other <user.1234.ain> (a comment)","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -15372,7 +15372,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay">> = iolist_to_binary(join(re:split("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -15953,7 +15953,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay">> = iolist_to_binary(join(re:split("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -16534,7 +16534,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay">> = iolist_to_binary(join(re:split("\"/s=user/ou=host/o=place/prmd=uu.yy/admd= /c=gb/\"-re.lay","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -17115,7 +17115,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"A missing angle <user.where">> = iolist_to_binary(join(re:split("A missing angle <user.where","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -17696,7 +17696,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"A missing angle <user.where">> = iolist_to_binary(join(re:split("A missing angle <user.where","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -18277,7 +18277,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"A missing angle <user.where">> = iolist_to_binary(join(re:split("A missing angle <user.where","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -18858,7 +18858,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -19439,7 +19439,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -20020,7 +20020,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -20601,7 +20601,7 @@ run3() -> # address spec > # > # name and address -)",[extended]))), +)",[extended]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -21182,7 +21182,7 @@ run3() -> # address spec > # > # name and address -)",[extended,trim]))), +)",[extended,trim]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -21763,7 +21763,7 @@ run3() -> # address spec > # > # name and address -)",[extended,{parts,2}]))), +)",[extended,{parts,2}]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","[\\040\\t]* # Nab whitespace. (?: \\( # ( @@ -22344,5763 +22344,5763 @@ run3() -> # address spec > # > # name and address -)",[extended]))), - <<"abcdefpqrxyz0AB">> = iolist_to_binary(join(re:split("abcdefpqrxyz0AB","abc\\0def\\00pqr\\000xyz\\0000AB",[trim]))), +)",[extended]))), + <<"abcdefpqrxyz0AB">> = iolist_to_binary(join(re:split("abcdefpqrxyz0AB","abc\\0def\\00pqr\\000xyz\\0000AB",[trim]))), <<"abcdefpqrxyz0AB">> = iolist_to_binary(join(re:split("abcdefpqrxyz0AB","abc\\0def\\00pqr\\000xyz\\0000AB",[{parts, - 2}]))), - <<"abcdefpqrxyz0AB">> = iolist_to_binary(join(re:split("abcdefpqrxyz0AB","abc\\0def\\00pqr\\000xyz\\0000AB",[]))), - <<"abc456 abcdefpqrxyz0ABCDE">> = iolist_to_binary(join(re:split("abc456 abcdefpqrxyz0ABCDE","abc\\0def\\00pqr\\000xyz\\0000AB",[trim]))), + 2}]))), + <<"abcdefpqrxyz0AB">> = iolist_to_binary(join(re:split("abcdefpqrxyz0AB","abc\\0def\\00pqr\\000xyz\\0000AB",[]))), + <<"abc456 abcdefpqrxyz0ABCDE">> = iolist_to_binary(join(re:split("abc456 abcdefpqrxyz0ABCDE","abc\\0def\\00pqr\\000xyz\\0000AB",[trim]))), <<"abc456 abcdefpqrxyz0ABCDE">> = iolist_to_binary(join(re:split("abc456 abcdefpqrxyz0ABCDE","abc\\0def\\00pqr\\000xyz\\0000AB",[{parts, - 2}]))), - <<"abc456 abcdefpqrxyz0ABCDE">> = iolist_to_binary(join(re:split("abc456 abcdefpqrxyz0ABCDE","abc\\0def\\00pqr\\000xyz\\0000AB",[]))), - <<"abc
efpqr0xyz00AB">> = iolist_to_binary(join(re:split("abc
efpqr0xyz00AB","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[trim]))), + 2}]))), + <<"abc456 abcdefpqrxyz0ABCDE">> = iolist_to_binary(join(re:split("abc456 abcdefpqrxyz0ABCDE","abc\\0def\\00pqr\\000xyz\\0000AB",[]))), + <<"abc
efpqr0xyz00AB">> = iolist_to_binary(join(re:split("abc
efpqr0xyz00AB","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[trim]))), <<"abc
efpqr0xyz00AB">> = iolist_to_binary(join(re:split("abc
efpqr0xyz00AB","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[{parts, - 2}]))), - <<"abc
efpqr0xyz00AB">> = iolist_to_binary(join(re:split("abc
efpqr0xyz00AB","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[]))), - <<"abc456 abc
efpqr0xyz00ABCDE">> = iolist_to_binary(join(re:split("abc456 abc
efpqr0xyz00ABCDE","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[trim]))), + 2}]))), + <<"abc
efpqr0xyz00AB">> = iolist_to_binary(join(re:split("abc
efpqr0xyz00AB","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[]))), + <<"abc456 abc
efpqr0xyz00ABCDE">> = iolist_to_binary(join(re:split("abc456 abc
efpqr0xyz00ABCDE","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[trim]))), <<"abc456 abc
efpqr0xyz00ABCDE">> = iolist_to_binary(join(re:split("abc456 abc
efpqr0xyz00ABCDE","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[{parts, - 2}]))), - <<"abc456 abc
efpqr0xyz00ABCDE">> = iolist_to_binary(join(re:split("abc456 abc
efpqr0xyz00ABCDE","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[]))), - <<"A">> = iolist_to_binary(join(re:split("A","^[\\000-\\037]",[trim]))), + 2}]))), + <<"abc456 abc
efpqr0xyz00ABCDE">> = iolist_to_binary(join(re:split("abc456 abc
efpqr0xyz00ABCDE","abc\\x0def\\x00pqr\\x000xyz\\x0000AB",[]))), + <<"A">> = iolist_to_binary(join(re:split("A","^[\\000-\\037]",[trim]))), <<"A">> = iolist_to_binary(join(re:split("A","^[\\000-\\037]",[{parts, - 2}]))), - <<"A">> = iolist_to_binary(join(re:split("A","^[\\000-\\037]",[]))), - <<":B">> = iolist_to_binary(join(re:split("B","^[\\000-\\037]",[trim]))), + 2}]))), + <<"A">> = iolist_to_binary(join(re:split("A","^[\\000-\\037]",[]))), + <<":B">> = iolist_to_binary(join(re:split("B","^[\\000-\\037]",[trim]))), <<":B">> = iolist_to_binary(join(re:split("B","^[\\000-\\037]",[{parts, - 2}]))), - <<":B">> = iolist_to_binary(join(re:split("B","^[\\000-\\037]",[]))), - <<":C">> = iolist_to_binary(join(re:split("C","^[\\000-\\037]",[trim]))), + 2}]))), + <<":B">> = iolist_to_binary(join(re:split("B","^[\\000-\\037]",[]))), + <<":C">> = iolist_to_binary(join(re:split("C","^[\\000-\\037]",[trim]))), <<":C">> = iolist_to_binary(join(re:split("C","^[\\000-\\037]",[{parts, - 2}]))), - <<":C">> = iolist_to_binary(join(re:split("C","^[\\000-\\037]",[]))), - <<"">> = iolist_to_binary(join(re:split("","\\0*",[trim]))), + 2}]))), + <<":C">> = iolist_to_binary(join(re:split("C","^[\\000-\\037]",[]))), + <<"">> = iolist_to_binary(join(re:split("","\\0*",[trim]))), <<"">> = iolist_to_binary(join(re:split("","\\0*",[{parts, - 2}]))), - <<"">> = iolist_to_binary(join(re:split("","\\0*",[]))), - <<"The AZ">> = iolist_to_binary(join(re:split("The AZ","A\\x0{2,3}Z",[trim]))), + 2}]))), + <<"">> = iolist_to_binary(join(re:split("","\\0*",[]))), + <<"The AZ">> = iolist_to_binary(join(re:split("The AZ","A\\x0{2,3}Z",[trim]))), <<"The AZ">> = iolist_to_binary(join(re:split("The AZ","A\\x0{2,3}Z",[{parts, - 2}]))), - <<"The AZ">> = iolist_to_binary(join(re:split("The AZ","A\\x0{2,3}Z",[]))), - <<"An AZ">> = iolist_to_binary(join(re:split("An AZ","A\\x0{2,3}Z",[trim]))), + 2}]))), + <<"The AZ">> = iolist_to_binary(join(re:split("The AZ","A\\x0{2,3}Z",[]))), + <<"An AZ">> = iolist_to_binary(join(re:split("An AZ","A\\x0{2,3}Z",[trim]))), <<"An AZ">> = iolist_to_binary(join(re:split("An AZ","A\\x0{2,3}Z",[{parts, - 2}]))), - <<"An AZ">> = iolist_to_binary(join(re:split("An AZ","A\\x0{2,3}Z",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","A\\x0{2,3}Z",[trim]))), + 2}]))), + <<"An AZ">> = iolist_to_binary(join(re:split("An AZ","A\\x0{2,3}Z",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","A\\x0{2,3}Z",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","A\\x0{2,3}Z",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","A\\x0{2,3}Z",[]))), - <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","A\\x0{2,3}Z",[]))), + <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[trim]))), <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[{parts, - 2}]))), - <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[]))), - <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[trim]))), + 2}]))), + <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[]))), + <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[trim]))), <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[{parts, - 2}]))), - <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[]))), - <<":cow:bell">> = iolist_to_binary(join(re:split("cowcowbell","^(cow|)\\1(bell)",[trim]))), + 2}]))), + <<"AZ">> = iolist_to_binary(join(re:split("AZ","A\\x0{2,3}Z",[]))), + <<":cow:bell">> = iolist_to_binary(join(re:split("cowcowbell","^(cow|)\\1(bell)",[trim]))), <<":cow:bell:">> = iolist_to_binary(join(re:split("cowcowbell","^(cow|)\\1(bell)",[{parts, - 2}]))), - <<":cow:bell:">> = iolist_to_binary(join(re:split("cowcowbell","^(cow|)\\1(bell)",[]))), - <<"::bell">> = iolist_to_binary(join(re:split("bell","^(cow|)\\1(bell)",[trim]))), + 2}]))), + <<":cow:bell:">> = iolist_to_binary(join(re:split("cowcowbell","^(cow|)\\1(bell)",[]))), + <<"::bell">> = iolist_to_binary(join(re:split("bell","^(cow|)\\1(bell)",[trim]))), <<"::bell:">> = iolist_to_binary(join(re:split("bell","^(cow|)\\1(bell)",[{parts, - 2}]))), - <<"::bell:">> = iolist_to_binary(join(re:split("bell","^(cow|)\\1(bell)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(cow|)\\1(bell)",[trim]))), + 2}]))), + <<"::bell:">> = iolist_to_binary(join(re:split("bell","^(cow|)\\1(bell)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(cow|)\\1(bell)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(cow|)\\1(bell)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(cow|)\\1(bell)",[]))), - <<"cowbell">> = iolist_to_binary(join(re:split("cowbell","^(cow|)\\1(bell)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(cow|)\\1(bell)",[]))), + <<"cowbell">> = iolist_to_binary(join(re:split("cowbell","^(cow|)\\1(bell)",[trim]))), <<"cowbell">> = iolist_to_binary(join(re:split("cowbell","^(cow|)\\1(bell)",[{parts, - 2}]))), - <<"cowbell">> = iolist_to_binary(join(re:split("cowbell","^(cow|)\\1(bell)",[]))), - <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[trim]))), + 2}]))), + <<"cowbell">> = iolist_to_binary(join(re:split("cowbell","^(cow|)\\1(bell)",[]))), + <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[trim]))), <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[{parts, - 2}]))), - <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abc","^\\s",[trim]))), + 2}]))), + <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abc","^\\s",[trim]))), <<":abc">> = iolist_to_binary(join(re:split("abc","^\\s",[{parts, - 2}]))), - <<":abc">> = iolist_to_binary(join(re:split("abc","^\\s",[]))), + 2}]))), + <<":abc">> = iolist_to_binary(join(re:split("abc","^\\s",[]))), <<":abc">> = iolist_to_binary(join(re:split(" -abc","^\\s",[trim]))), +abc","^\\s",[trim]))), <<":abc">> = iolist_to_binary(join(re:split(" -abc","^\\s",[{parts,2}]))), +abc","^\\s",[{parts,2}]))), <<":abc">> = iolist_to_binary(join(re:split(" -abc","^\\s",[]))), - <<":abc">> = iolist_to_binary(join(re:split("
abc","^\\s",[trim]))), +abc","^\\s",[]))), + <<":abc">> = iolist_to_binary(join(re:split("
abc","^\\s",[trim]))), <<":abc">> = iolist_to_binary(join(re:split("
abc","^\\s",[{parts, - 2}]))), - <<":abc">> = iolist_to_binary(join(re:split("
abc","^\\s",[]))), - <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[trim]))), + 2}]))), + <<":abc">> = iolist_to_binary(join(re:split("
abc","^\\s",[]))), + <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[trim]))), <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[{parts, - 2}]))), - <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\s",[trim]))), + 2}]))), + <<":abc">> = iolist_to_binary(join(re:split(" abc","^\\s",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\s",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\s",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\s",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^\\s",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\s",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^\\s",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^\\s",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^\\s",[]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^\\s",[]))), ok. run4() -> <<"">> = iolist_to_binary(join(re:split("abc","^a b - c",[extended,trim]))), + c",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^a b - c",[extended,{parts,2}]))), + c",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc","^a b - c",[extended]))), - <<":a">> = iolist_to_binary(join(re:split("ab","^(a|)\\1*b",[trim]))), + c",[extended]))), + <<":a">> = iolist_to_binary(join(re:split("ab","^(a|)\\1*b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ab","^(a|)\\1*b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ab","^(a|)\\1*b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1*b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ab","^(a|)\\1*b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1*b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1*b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1*b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1*b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1*b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1*b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1*b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1*b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1*b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1*b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1*b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1*b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1*b",[]))), - <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1*b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1*b",[]))), + <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1*b",[trim]))), <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1*b",[{parts, - 2}]))), - <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1*b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aab","^(a|)\\1+b",[trim]))), + 2}]))), + <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1*b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aab","^(a|)\\1+b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aab","^(a|)\\1+b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aab","^(a|)\\1+b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1+b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aab","^(a|)\\1+b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1+b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1+b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1+b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1+b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1+b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1+b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1+b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1+b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1+b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1+b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1+b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1+b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1+b",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1+b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1+b",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1+b",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1+b",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1+b",[]))), - <<":a">> = iolist_to_binary(join(re:split("ab","^(a|)\\1?b",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1+b",[]))), + <<":a">> = iolist_to_binary(join(re:split("ab","^(a|)\\1?b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ab","^(a|)\\1?b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ab","^(a|)\\1?b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aab","^(a|)\\1?b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ab","^(a|)\\1?b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aab","^(a|)\\1?b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aab","^(a|)\\1?b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aab","^(a|)\\1?b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1?b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aab","^(a|)\\1?b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1?b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1?b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1?b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1?b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1?b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1?b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1?b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1?b",[]))), - <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1?b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1?b",[]))), + <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1?b",[trim]))), <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1?b",[{parts, - 2}]))), - <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1?b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2}b",[trim]))), + 2}]))), + <<"acb">> = iolist_to_binary(join(re:split("acb","^(a|)\\1?b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2}b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2}b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2}b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2}b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2}b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2}b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2}b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2}b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2}b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2}b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2}b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2}b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2}b",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2}b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2}b",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2}b",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2}b",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2}b",[]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2}b",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2}b",[]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2}b",[trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2}b",[{parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2}b",[]))), - <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2}b",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2}b",[]))), + <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2}b",[trim]))), <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2}b",[{parts, - 2}]))), - <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2}b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2}b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2,3}b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2,3}b",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaab","^(a|)\\1{2,3}b",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2,3}b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2,3}b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a|)\\1{2,3}b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2,3}b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2,3}b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","^(a|)\\1{2,3}b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2,3}b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2,3}b",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a|)\\1{2,3}b",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2,3}b",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2,3}b",[]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a|)\\1{2,3}b",[]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2,3}b",[trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2,3}b",[]))), - <<"aaaaab">> = iolist_to_binary(join(re:split("aaaaab","^(a|)\\1{2,3}b",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^(a|)\\1{2,3}b",[]))), + <<"aaaaab">> = iolist_to_binary(join(re:split("aaaaab","^(a|)\\1{2,3}b",[trim]))), <<"aaaaab">> = iolist_to_binary(join(re:split("aaaaab","^(a|)\\1{2,3}b",[{parts, - 2}]))), - <<"aaaaab">> = iolist_to_binary(join(re:split("aaaaab","^(a|)\\1{2,3}b",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[trim]))), + 2}]))), + <<"aaaaab">> = iolist_to_binary(join(re:split("aaaaab","^(a|)\\1{2,3}b",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbc","ab{1,3}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbc","ab{1,3}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbc","ab{1,3}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbc","ab{1,3}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbc","ab{1,3}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbc","ab{1,3}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbc","ab{1,3}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbc","ab{1,3}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbc","ab{1,3}bc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{1,3}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbc","ab{1,3}bc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{1,3}bc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{1,3}bc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{1,3}bc",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","ab{1,3}bc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{1,3}bc",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","ab{1,3}bc",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","ab{1,3}bc",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","ab{1,3}bc",[]))), - <<"abbbbbc">> = iolist_to_binary(join(re:split("abbbbbc","ab{1,3}bc",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","ab{1,3}bc",[]))), + <<"abbbbbc">> = iolist_to_binary(join(re:split("abbbbbc","ab{1,3}bc",[trim]))), <<"abbbbbc">> = iolist_to_binary(join(re:split("abbbbbc","ab{1,3}bc",[{parts, - 2}]))), - <<"abbbbbc">> = iolist_to_binary(join(re:split("abbbbbc","ab{1,3}bc",[]))), - <<":track1:title:Blah blah blah">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[trim]))), + 2}]))), + <<"abbbbbc">> = iolist_to_binary(join(re:split("abbbbbc","ab{1,3}bc",[]))), + <<":track1:title:Blah blah blah">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[trim]))), <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[{parts, - 2}]))), - <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[]))), + 2}]))), + <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[]))), <<":track1:title:Blah blah blah">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[caseless, - trim]))), + trim]))), <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[caseless, {parts, - 2}]))), - <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[caseless]))), + 2}]))), + <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[T ]+(.*)",[caseless]))), <<":track1:title:Blah blah blah">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[t ]+(.*)",[caseless, - trim]))), + trim]))), <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[t ]+(.*)",[caseless, {parts, - 2}]))), - <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[t ]+(.*)",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[trim]))), + 2}]))), + <<":track1:title:Blah blah blah:">> = iolist_to_binary(join(re:split("track1.title:TBlah blah blah","([^.]*)\\.([^:]*):[t ]+(.*)",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-c]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-c]+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-c]+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-c]+$",[]))), - <<"wxy">> = iolist_to_binary(join(re:split("wxy","^[W-c]+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-c]+$",[]))), + <<"wxy">> = iolist_to_binary(join(re:split("wxy","^[W-c]+$",[trim]))), <<"wxy">> = iolist_to_binary(join(re:split("wxy","^[W-c]+$",[{parts, - 2}]))), - <<"wxy">> = iolist_to_binary(join(re:split("wxy","^[W-c]+$",[]))), + 2}]))), + <<"wxy">> = iolist_to_binary(join(re:split("wxy","^[W-c]+$",[]))), <<"">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[W-c]+$",[caseless]))), <<"">> = iolist_to_binary(join(re:split("wxy_^ABC","^[W-c]+$",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("wxy_^ABC","^[W-c]+$",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("wxy_^ABC","^[W-c]+$",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("wxy_^ABC","^[W-c]+$",[caseless]))), <<"">> = iolist_to_binary(join(re:split("WXY_^abc","^[\\x3f-\\x5F]+$",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[\\x3f-\\x5F]+$",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[\\x3f-\\x5F]+$",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("WXY_^abc","^[\\x3f-\\x5F]+$",[caseless]))), <<"">> = iolist_to_binary(join(re:split("wxy_^ABC","^[\\x3f-\\x5F]+$",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("wxy_^ABC","^[\\x3f-\\x5F]+$",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("wxy_^ABC","^[\\x3f-\\x5F]+$",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("wxy_^ABC","^[\\x3f-\\x5F]+$",[caseless]))), <<"">> = iolist_to_binary(join(re:split("abc","^abc$",[multiline, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[multiline, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[multiline]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[multiline]))), <<"qqq ">> = iolist_to_binary(join(re:split("qqq -abc","^abc$",[multiline,trim]))), +abc","^abc$",[multiline,trim]))), <<"qqq :">> = iolist_to_binary(join(re:split("qqq -abc","^abc$",[multiline,{parts,2}]))), +abc","^abc$",[multiline,{parts,2}]))), <<"qqq :">> = iolist_to_binary(join(re:split("qqq -abc","^abc$",[multiline]))), +abc","^abc$",[multiline]))), <<": zzz">> = iolist_to_binary(join(re:split("abc -zzz","^abc$",[multiline,trim]))), +zzz","^abc$",[multiline,trim]))), <<": zzz">> = iolist_to_binary(join(re:split("abc -zzz","^abc$",[multiline,{parts,2}]))), +zzz","^abc$",[multiline,{parts,2}]))), <<": zzz">> = iolist_to_binary(join(re:split("abc -zzz","^abc$",[multiline]))), +zzz","^abc$",[multiline]))), <<"qqq : zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","^abc$",[multiline,trim]))), +zzz","^abc$",[multiline,trim]))), <<"qqq : zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","^abc$",[multiline,{parts,2}]))), +zzz","^abc$",[multiline,{parts,2}]))), <<"qqq : zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","^abc$",[multiline]))), - <<"">> = iolist_to_binary(join(re:split("abc","^abc$",[trim]))), +zzz","^abc$",[multiline]))), + <<"">> = iolist_to_binary(join(re:split("abc","^abc$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","^abc$",[trim]))), +abc","^abc$",[trim]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","^abc$",[{parts,2}]))), +abc","^abc$",[{parts,2}]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","^abc$",[]))), +abc","^abc$",[]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","^abc$",[trim]))), +zzz","^abc$",[trim]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","^abc$",[{parts,2}]))), +zzz","^abc$",[{parts,2}]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","^abc$",[]))), +zzz","^abc$",[]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","^abc$",[trim]))), +zzz","^abc$",[trim]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","^abc$",[{parts,2}]))), +zzz","^abc$",[{parts,2}]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","^abc$",[]))), +zzz","^abc$",[]))), <<"">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline]))), <<"">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\Z",[multiline]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\Z",[multiline, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\Z",[multiline, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\Z",[multiline]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\Z",[multiline]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","\\Aabc\\Z",[multiline,trim]))), +abc","\\Aabc\\Z",[multiline,trim]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","\\Aabc\\Z",[multiline,{parts,2}]))), +abc","\\Aabc\\Z",[multiline,{parts,2}]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","\\Aabc\\Z",[multiline]))), +abc","\\Aabc\\Z",[multiline]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","\\Aabc\\Z",[multiline,trim]))), +zzz","\\Aabc\\Z",[multiline,trim]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","\\Aabc\\Z",[multiline,{parts,2}]))), +zzz","\\Aabc\\Z",[multiline,{parts,2}]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","\\Aabc\\Z",[multiline]))), +zzz","\\Aabc\\Z",[multiline]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","\\Aabc\\Z",[multiline,trim]))), +zzz","\\Aabc\\Z",[multiline,trim]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","\\Aabc\\Z",[multiline,{parts,2}]))), +zzz","\\Aabc\\Z",[multiline,{parts,2}]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","\\Aabc\\Z",[multiline]))), +zzz","\\Aabc\\Z",[multiline]))), <<":f">> = iolist_to_binary(join(re:split("abc -def","\\A(.)*\\Z",[dotall,trim]))), +def","\\A(.)*\\Z",[dotall,trim]))), <<":f:">> = iolist_to_binary(join(re:split("abc -def","\\A(.)*\\Z",[dotall,{parts,2}]))), +def","\\A(.)*\\Z",[dotall,{parts,2}]))), <<":f:">> = iolist_to_binary(join(re:split("abc -def","\\A(.)*\\Z",[dotall]))), +def","\\A(.)*\\Z",[dotall]))), <<":s">> = iolist_to_binary(join(re:split("*** Failers","\\A(.)*\\Z",[multiline, - trim]))), + trim]))), <<":s:">> = iolist_to_binary(join(re:split("*** Failers","\\A(.)*\\Z",[multiline, {parts, - 2}]))), - <<":s:">> = iolist_to_binary(join(re:split("*** Failers","\\A(.)*\\Z",[multiline]))), + 2}]))), + <<":s:">> = iolist_to_binary(join(re:split("*** Failers","\\A(.)*\\Z",[multiline]))), <<"abc def">> = iolist_to_binary(join(re:split("abc -def","\\A(.)*\\Z",[multiline,trim]))), +def","\\A(.)*\\Z",[multiline,trim]))), <<"abc def">> = iolist_to_binary(join(re:split("abc -def","\\A(.)*\\Z",[multiline,{parts,2}]))), +def","\\A(.)*\\Z",[multiline,{parts,2}]))), <<"abc def">> = iolist_to_binary(join(re:split("abc -def","\\A(.)*\\Z",[multiline]))), - <<"::c">> = iolist_to_binary(join(re:split("b::c","(?:b)|(?::+)",[trim]))), +def","\\A(.)*\\Z",[multiline]))), + <<"::c">> = iolist_to_binary(join(re:split("b::c","(?:b)|(?::+)",[trim]))), <<":::c">> = iolist_to_binary(join(re:split("b::c","(?:b)|(?::+)",[{parts, - 2}]))), - <<"::c">> = iolist_to_binary(join(re:split("b::c","(?:b)|(?::+)",[]))), - <<"c">> = iolist_to_binary(join(re:split("c::b","(?:b)|(?::+)",[trim]))), + 2}]))), + <<"::c">> = iolist_to_binary(join(re:split("b::c","(?:b)|(?::+)",[]))), + <<"c">> = iolist_to_binary(join(re:split("c::b","(?:b)|(?::+)",[trim]))), <<"c:b">> = iolist_to_binary(join(re:split("c::b","(?:b)|(?::+)",[{parts, - 2}]))), - <<"c::">> = iolist_to_binary(join(re:split("c::b","(?:b)|(?::+)",[]))), - <<"">> = iolist_to_binary(join(re:split("az-","[-az]+",[trim]))), + 2}]))), + <<"c::">> = iolist_to_binary(join(re:split("c::b","(?:b)|(?::+)",[]))), + <<"">> = iolist_to_binary(join(re:split("az-","[-az]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("az-","[-az]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("az-","[-az]+",[]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[-az]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("az-","[-az]+",[]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[-az]+",[trim]))), <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[-az]+",[{parts, - 2}]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[-az]+",[]))), - <<"b">> = iolist_to_binary(join(re:split("b","[-az]+",[trim]))), + 2}]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[-az]+",[]))), + <<"b">> = iolist_to_binary(join(re:split("b","[-az]+",[trim]))), <<"b">> = iolist_to_binary(join(re:split("b","[-az]+",[{parts, - 2}]))), - <<"b">> = iolist_to_binary(join(re:split("b","[-az]+",[]))), + 2}]))), + <<"b">> = iolist_to_binary(join(re:split("b","[-az]+",[]))), ok. run5() -> - <<"">> = iolist_to_binary(join(re:split("za-","[az-]+",[trim]))), + <<"">> = iolist_to_binary(join(re:split("za-","[az-]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("za-","[az-]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("za-","[az-]+",[]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[az-]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("za-","[az-]+",[]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[az-]+",[trim]))), <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[az-]+",[{parts, - 2}]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[az-]+",[]))), - <<"b">> = iolist_to_binary(join(re:split("b","[az-]+",[trim]))), + 2}]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[az-]+",[]))), + <<"b">> = iolist_to_binary(join(re:split("b","[az-]+",[trim]))), <<"b">> = iolist_to_binary(join(re:split("b","[az-]+",[{parts, - 2}]))), - <<"b">> = iolist_to_binary(join(re:split("b","[az-]+",[]))), - <<"">> = iolist_to_binary(join(re:split("a-z","[a\\-z]+",[trim]))), + 2}]))), + <<"b">> = iolist_to_binary(join(re:split("b","[az-]+",[]))), + <<"">> = iolist_to_binary(join(re:split("a-z","[a\\-z]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-z","[a\\-z]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-z","[a\\-z]+",[]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[a\\-z]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-z","[a\\-z]+",[]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[a\\-z]+",[trim]))), <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[a\\-z]+",[{parts, - 2}]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[a\\-z]+",[]))), - <<"b">> = iolist_to_binary(join(re:split("b","[a\\-z]+",[trim]))), + 2}]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[a\\-z]+",[]))), + <<"b">> = iolist_to_binary(join(re:split("b","[a\\-z]+",[trim]))), <<"b">> = iolist_to_binary(join(re:split("b","[a\\-z]+",[{parts, - 2}]))), - <<"b">> = iolist_to_binary(join(re:split("b","[a\\-z]+",[]))), - <<"">> = iolist_to_binary(join(re:split("abcdxyz","[a-z]+",[trim]))), + 2}]))), + <<"b">> = iolist_to_binary(join(re:split("b","[a\\-z]+",[]))), + <<"">> = iolist_to_binary(join(re:split("abcdxyz","[a-z]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcdxyz","[a-z]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcdxyz","[a-z]+",[]))), - <<"">> = iolist_to_binary(join(re:split("12-34","[\\d-]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcdxyz","[a-z]+",[]))), + <<"">> = iolist_to_binary(join(re:split("12-34","[\\d-]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("12-34","[\\d-]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12-34","[\\d-]+",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12-34","[\\d-]+",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-]+",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-]+",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-]+",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-]+",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-]+",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-]+",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-]+",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-]+",[]))), - <<"">> = iolist_to_binary(join(re:split("12-34z","[\\d-z]+",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-]+",[]))), + <<"">> = iolist_to_binary(join(re:split("12-34z","[\\d-z]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("12-34z","[\\d-z]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12-34z","[\\d-z]+",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-z]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12-34z","[\\d-z]+",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-z]+",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-z]+",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-z]+",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-z]+",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\d-z]+",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-z]+",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-z]+",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-z]+",[]))), - <<": ">> = iolist_to_binary(join(re:split("\\ ","\\x5c",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","[\\d-z]+",[]))), + <<": ">> = iolist_to_binary(join(re:split("\\ ","\\x5c",[trim]))), <<": ">> = iolist_to_binary(join(re:split("\\ ","\\x5c",[{parts, - 2}]))), - <<": ">> = iolist_to_binary(join(re:split("\\ ","\\x5c",[]))), - <<"the:oo">> = iolist_to_binary(join(re:split("the Zoo","\\x20Z",[trim]))), + 2}]))), + <<": ">> = iolist_to_binary(join(re:split("\\ ","\\x5c",[]))), + <<"the:oo">> = iolist_to_binary(join(re:split("the Zoo","\\x20Z",[trim]))), <<"the:oo">> = iolist_to_binary(join(re:split("the Zoo","\\x20Z",[{parts, - 2}]))), - <<"the:oo">> = iolist_to_binary(join(re:split("the Zoo","\\x20Z",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\x20Z",[trim]))), + 2}]))), + <<"the:oo">> = iolist_to_binary(join(re:split("the Zoo","\\x20Z",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\x20Z",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\x20Z",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\x20Z",[]))), - <<"Zulu">> = iolist_to_binary(join(re:split("Zulu","\\x20Z",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\x20Z",[]))), + <<"Zulu">> = iolist_to_binary(join(re:split("Zulu","\\x20Z",[trim]))), <<"Zulu">> = iolist_to_binary(join(re:split("Zulu","\\x20Z",[{parts, - 2}]))), - <<"Zulu">> = iolist_to_binary(join(re:split("Zulu","\\x20Z",[]))), + 2}]))), + <<"Zulu">> = iolist_to_binary(join(re:split("Zulu","\\x20Z",[]))), <<":abc">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[caseless, - trim]))), + trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[caseless, {parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[caseless]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[caseless]))), <<":ABC">> = iolist_to_binary(join(re:split("ABCabc","(abc)\\1",[caseless, - trim]))), + trim]))), <<":ABC:">> = iolist_to_binary(join(re:split("ABCabc","(abc)\\1",[caseless, {parts, - 2}]))), - <<":ABC:">> = iolist_to_binary(join(re:split("ABCabc","(abc)\\1",[caseless]))), + 2}]))), + <<":ABC:">> = iolist_to_binary(join(re:split("ABCabc","(abc)\\1",[caseless]))), <<":abc">> = iolist_to_binary(join(re:split("abcABC","(abc)\\1",[caseless, - trim]))), + trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcABC","(abc)\\1",[caseless, {parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcABC","(abc)\\1",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("ab{3cd","ab{3cd",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcABC","(abc)\\1",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("ab{3cd","ab{3cd",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab{3cd","ab{3cd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab{3cd","ab{3cd",[]))), - <<"">> = iolist_to_binary(join(re:split("ab{3,cd","ab{3,cd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab{3cd","ab{3cd",[]))), + <<"">> = iolist_to_binary(join(re:split("ab{3,cd","ab{3,cd",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab{3,cd","ab{3,cd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab{3,cd","ab{3,cd",[]))), - <<"">> = iolist_to_binary(join(re:split("ab{3,4a}cd","ab{3,4a}cd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab{3,cd","ab{3,cd",[]))), + <<"">> = iolist_to_binary(join(re:split("ab{3,4a}cd","ab{3,4a}cd",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab{3,4a}cd","ab{3,4a}cd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab{3,4a}cd","ab{3,4a}cd",[]))), - <<"">> = iolist_to_binary(join(re:split("{4,5a}bc","{4,5a}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab{3,4a}cd","ab{3,4a}cd",[]))), + <<"">> = iolist_to_binary(join(re:split("{4,5a}bc","{4,5a}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("{4,5a}bc","{4,5a}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("{4,5a}bc","{4,5a}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","abc$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("{4,5a}bc","{4,5a}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","abc$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","abc$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","abc$",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","abc$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","abc$",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","abc$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","abc$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","abc$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","abc$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[]))), <<"abc def">> = iolist_to_binary(join(re:split("abc -def","abc$",[trim]))), +def","abc$",[trim]))), <<"abc def">> = iolist_to_binary(join(re:split("abc -def","abc$",[{parts,2}]))), +def","abc$",[{parts,2}]))), <<"abc def">> = iolist_to_binary(join(re:split("abc -def","abc$",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[trim]))), +def","abc$",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abc“","(abc)\\223",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcS","(abc)\\123",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abc“","(abc)\\223",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abc“","(abc)\\223",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abc“","(abc)\\223",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abc“","(abc)\\223",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcÓ","(abc)\\323",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abc@","(abc)\\100",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), - <<":A:B:C:D:E:F:G:H:I">> = iolist_to_binary(join(re:split("ABCDEFGHIHI","^(A)(B)(C)(D)(E)(F)(G)(H)(I)\\8\\9$",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1000",[]))), + <<":A:B:C:D:E:F:G:H:I">> = iolist_to_binary(join(re:split("ABCDEFGHIHI","^(A)(B)(C)(D)(E)(F)(G)(H)(I)\\8\\9$",[trim]))), <<":A:B:C:D:E:F:G:H:I:">> = iolist_to_binary(join(re:split("ABCDEFGHIHI","^(A)(B)(C)(D)(E)(F)(G)(H)(I)\\8\\9$",[{parts, - 2}]))), - <<":A:B:C:D:E:F:G:H:I:">> = iolist_to_binary(join(re:split("ABCDEFGHIHI","^(A)(B)(C)(D)(E)(F)(G)(H)(I)\\8\\9$",[]))), + 2}]))), + <<":A:B:C:D:E:F:G:H:I:">> = iolist_to_binary(join(re:split("ABCDEFGHIHI","^(A)(B)(C)(D)(E)(F)(G)(H)(I)\\8\\9$",[]))), ok. run6() -> - <<"">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[trim]))), + <<"">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[A\\8B\\9C]+$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[A\\8B\\9C]+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[A\\8B\\9C]+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[A\\8B\\9C]+$",[]))), - <<"">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[A\\8B\\9C]+$",[]))), + <<"">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[trim]))), <<":">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[]))), - <<":a:b:c:d:e:f:g:h:i:j:k:l">> = iolist_to_binary(join(re:split("abcdefghijkllS","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)\\12\\123",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A8B9C","^[A\\8B\\9C]+$",[]))), + <<":a:b:c:d:e:f:g:h:i:j:k:l">> = iolist_to_binary(join(re:split("abcdefghijkllS","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)\\12\\123",[trim]))), <<":a:b:c:d:e:f:g:h:i:j:k:l:">> = iolist_to_binary(join(re:split("abcdefghijkllS","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)\\12\\123",[{parts, - 2}]))), - <<":a:b:c:d:e:f:g:h:i:j:k:l:">> = iolist_to_binary(join(re:split("abcdefghijkllS","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)\\12\\123",[]))), + 2}]))), + <<":a:b:c:d:e:f:g:h:i:j:k:l:">> = iolist_to_binary(join(re:split("abcdefghijkllS","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)(l)\\12\\123",[]))), <<":a:b:c:d:e:f:g:h:i:j:k">> = iolist_to_binary(join(re:split("abcdefghijk -S","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\12\\123",[trim]))), +S","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\12\\123",[trim]))), <<":a:b:c:d:e:f:g:h:i:j:k:">> = iolist_to_binary(join(re:split("abcdefghijk -S","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\12\\123",[{parts,2}]))), +S","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\12\\123",[{parts,2}]))), <<":a:b:c:d:e:f:g:h:i:j:k:">> = iolist_to_binary(join(re:split("abcdefghijk -S","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\12\\123",[]))), - <<"">> = iolist_to_binary(join(re:split("abidef","ab\\idef",[trim]))), +S","(a)(b)(c)(d)(e)(f)(g)(h)(i)(j)(k)\\12\\123",[]))), + <<"">> = iolist_to_binary(join(re:split("abidef","ab\\idef",[trim]))), <<":">> = iolist_to_binary(join(re:split("abidef","ab\\idef",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abidef","ab\\idef",[]))), - <<"">> = iolist_to_binary(join(re:split("bc","a{0}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abidef","ab\\idef",[]))), + <<"">> = iolist_to_binary(join(re:split("bc","a{0}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("bc","a{0}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("bc","a{0}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("xyz","(a|(bc)){0,0}?xyz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("bc","a{0}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("xyz","(a|(bc)){0,0}?xyz",[trim]))), <<":::">> = iolist_to_binary(join(re:split("xyz","(a|(bc)){0,0}?xyz",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("xyz","(a|(bc)){0,0}?xyz",[]))), - <<"">> = iolist_to_binary(join(re:split("abcde","abc[\\10]de",[trim]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("xyz","(a|(bc)){0,0}?xyz",[]))), + <<"">> = iolist_to_binary(join(re:split("abcde","abc[\\10]de",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcde","abc[\\10]de",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcde","abc[\\10]de",[]))), - <<"">> = iolist_to_binary(join(re:split("abcde","abc[\\1]de",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcde","abc[\\10]de",[]))), + <<"">> = iolist_to_binary(join(re:split("abcde","abc[\\1]de",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcde","abc[\\1]de",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcde","abc[\\1]de",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcde","(abc)[\\1]de",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcde","abc[\\1]de",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcde","(abc)[\\1]de",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcde","(abc)[\\1]de",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcde","(abc)[\\1]de",[]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcde","(abc)[\\1]de",[]))), <<"">> = iolist_to_binary(join(re:split("a -b","(?s)a.b",[trim]))), +b","(?s)a.b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a -b","(?s)a.b",[{parts,2}]))), +b","(?s)a.b",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("a -b","(?s)a.b",[]))), - <<":b:a:NOT:cccc:d">> = iolist_to_binary(join(re:split("baNOTccccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), +b","(?s)a.b",[]))), + <<":b:a:NOT:cccc:d">> = iolist_to_binary(join(re:split("baNOTccccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<":b:a:NOT:cccc:d">> = iolist_to_binary(join(re:split("baNOTccccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<":b:a:NOT:cccc:d">> = iolist_to_binary(join(re:split("baNOTccccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<":b:a:NOT:ccc:d">> = iolist_to_binary(join(re:split("baNOTcccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<":b:a:NOT:cccc:d">> = iolist_to_binary(join(re:split("baNOTccccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<":b:a:NOT:ccc:d">> = iolist_to_binary(join(re:split("baNOTcccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<":b:a:NOT:ccc:d">> = iolist_to_binary(join(re:split("baNOTcccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<":b:a:NOT:ccc:d">> = iolist_to_binary(join(re:split("baNOTcccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<":b:a:NO:Tcc:d">> = iolist_to_binary(join(re:split("baNOTccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<":b:a:NOT:ccc:d">> = iolist_to_binary(join(re:split("baNOTcccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<":b:a:NO:Tcc:d">> = iolist_to_binary(join(re:split("baNOTccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<":b:a:NO:Tcc:d">> = iolist_to_binary(join(re:split("baNOTccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<":b:a:NO:Tcc:d">> = iolist_to_binary(join(re:split("baNOTccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<":b:a::ccc:d">> = iolist_to_binary(join(re:split("bacccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<":b:a:NO:Tcc:d">> = iolist_to_binary(join(re:split("baNOTccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<":b:a::ccc:d">> = iolist_to_binary(join(re:split("bacccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<":b:a::ccc:d">> = iolist_to_binary(join(re:split("bacccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<":b:a::ccc:d">> = iolist_to_binary(join(re:split("bacccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<":*:*:* Fail:ers">> = iolist_to_binary(join(re:split("*** Failers","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<":b:a::ccc:d">> = iolist_to_binary(join(re:split("bacccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<":*:*:* Fail:ers">> = iolist_to_binary(join(re:split("*** Failers","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<":*:*:* Fail:ers:">> = iolist_to_binary(join(re:split("*** Failers","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<":*:*:* Fail:ers:">> = iolist_to_binary(join(re:split("*** Failers","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<"anything">> = iolist_to_binary(join(re:split("anything","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<":*:*:* Fail:ers:">> = iolist_to_binary(join(re:split("*** Failers","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<"anything">> = iolist_to_binary(join(re:split("anything","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<"anything">> = iolist_to_binary(join(re:split("anything","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<"anything">> = iolist_to_binary(join(re:split("anything","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<"bc">> = iolist_to_binary(join(re:split("bc","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<"anything">> = iolist_to_binary(join(re:split("anything","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<"bc">> = iolist_to_binary(join(re:split("bc","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<"bc">> = iolist_to_binary(join(re:split("bc","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<"bc">> = iolist_to_binary(join(re:split("bc","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<"baccd">> = iolist_to_binary(join(re:split("baccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), + 2}]))), + <<"bc">> = iolist_to_binary(join(re:split("bc","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<"baccd">> = iolist_to_binary(join(re:split("baccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[trim]))), <<"baccd">> = iolist_to_binary(join(re:split("baccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[{parts, - 2}]))), - <<"baccd">> = iolist_to_binary(join(re:split("baccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), - <<"">> = iolist_to_binary(join(re:split("Abc","[^a]",[trim]))), + 2}]))), + <<"baccd">> = iolist_to_binary(join(re:split("baccd","^([^a])([^\\b])([^c]*)([^d]{3,4})",[]))), + <<"">> = iolist_to_binary(join(re:split("Abc","[^a]",[trim]))), <<":bc">> = iolist_to_binary(join(re:split("Abc","[^a]",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("Abc","[^a]",[]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("Abc","[^a]",[]))), <<"A">> = iolist_to_binary(join(re:split("Abc","[^a]",[caseless, - trim]))), + trim]))), <<"A:c">> = iolist_to_binary(join(re:split("Abc","[^a]",[caseless, {parts, - 2}]))), - <<"A::">> = iolist_to_binary(join(re:split("Abc","[^a]",[caseless]))), - <<":a">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[trim]))), + 2}]))), + <<"A::">> = iolist_to_binary(join(re:split("Abc","[^a]",[caseless]))), + <<":a">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[trim]))), <<":aAbc">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[]))), <<"AAAaA">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[caseless, - trim]))), + trim]))), <<"AAAaA:">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[caseless, {parts, - 2}]))), - <<"AAAaA:">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[caseless]))), + 2}]))), + <<"AAAaA:">> = iolist_to_binary(join(re:split("AAAaAbc","[^a]+",[caseless]))), <<"">> = iolist_to_binary(join(re:split("bbb -ccc","[^a]+",[trim]))), +ccc","[^a]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("bbb -ccc","[^a]+",[{parts,2}]))), +ccc","[^a]+",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("bbb -ccc","[^a]+",[]))), - <<"ab">> = iolist_to_binary(join(re:split("abc","[^k]$",[trim]))), +ccc","[^a]+",[]))), + <<"ab">> = iolist_to_binary(join(re:split("abc","[^k]$",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abc","[^k]$",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abc","[^k]$",[]))), - <<"*** Failer">> = iolist_to_binary(join(re:split("*** Failers","[^k]$",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abc","[^k]$",[]))), + <<"*** Failer">> = iolist_to_binary(join(re:split("*** Failers","[^k]$",[trim]))), <<"*** Failer:">> = iolist_to_binary(join(re:split("*** Failers","[^k]$",[{parts, - 2}]))), - <<"*** Failer:">> = iolist_to_binary(join(re:split("*** Failers","[^k]$",[]))), - <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]$",[trim]))), + 2}]))), + <<"*** Failer:">> = iolist_to_binary(join(re:split("*** Failers","[^k]$",[]))), + <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]$",[trim]))), <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]$",[{parts, - 2}]))), - <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]$",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","[^k]{2,3}$",[trim]))), + 2}]))), + <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]$",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","[^k]{2,3}$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","[^k]{2,3}$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","[^k]{2,3}$",[]))), - <<"k">> = iolist_to_binary(join(re:split("kbc","[^k]{2,3}$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","[^k]{2,3}$",[]))), + <<"k">> = iolist_to_binary(join(re:split("kbc","[^k]{2,3}$",[trim]))), <<"k:">> = iolist_to_binary(join(re:split("kbc","[^k]{2,3}$",[{parts, - 2}]))), - <<"k:">> = iolist_to_binary(join(re:split("kbc","[^k]{2,3}$",[]))), - <<"k">> = iolist_to_binary(join(re:split("kabc","[^k]{2,3}$",[trim]))), + 2}]))), + <<"k:">> = iolist_to_binary(join(re:split("kbc","[^k]{2,3}$",[]))), + <<"k">> = iolist_to_binary(join(re:split("kabc","[^k]{2,3}$",[trim]))), <<"k:">> = iolist_to_binary(join(re:split("kabc","[^k]{2,3}$",[{parts, - 2}]))), - <<"k:">> = iolist_to_binary(join(re:split("kabc","[^k]{2,3}$",[]))), - <<"*** Fail">> = iolist_to_binary(join(re:split("*** Failers","[^k]{2,3}$",[trim]))), + 2}]))), + <<"k:">> = iolist_to_binary(join(re:split("kabc","[^k]{2,3}$",[]))), + <<"*** Fail">> = iolist_to_binary(join(re:split("*** Failers","[^k]{2,3}$",[trim]))), <<"*** Fail:">> = iolist_to_binary(join(re:split("*** Failers","[^k]{2,3}$",[{parts, - 2}]))), - <<"*** Fail:">> = iolist_to_binary(join(re:split("*** Failers","[^k]{2,3}$",[]))), - <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]{2,3}$",[trim]))), + 2}]))), + <<"*** Fail:">> = iolist_to_binary(join(re:split("*** Failers","[^k]{2,3}$",[]))), + <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]{2,3}$",[trim]))), <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]{2,3}$",[{parts, - 2}]))), - <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]{2,3}$",[]))), - <<"akb">> = iolist_to_binary(join(re:split("akb","[^k]{2,3}$",[trim]))), + 2}]))), + <<"abk">> = iolist_to_binary(join(re:split("abk","[^k]{2,3}$",[]))), + <<"akb">> = iolist_to_binary(join(re:split("akb","[^k]{2,3}$",[trim]))), <<"akb">> = iolist_to_binary(join(re:split("akb","[^k]{2,3}$",[{parts, - 2}]))), - <<"akb">> = iolist_to_binary(join(re:split("akb","[^k]{2,3}$",[]))), - <<"akk">> = iolist_to_binary(join(re:split("akk","[^k]{2,3}$",[trim]))), + 2}]))), + <<"akb">> = iolist_to_binary(join(re:split("akb","[^k]{2,3}$",[]))), + <<"akk">> = iolist_to_binary(join(re:split("akk","[^k]{2,3}$",[trim]))), <<"akk">> = iolist_to_binary(join(re:split("akk","[^k]{2,3}$",[{parts, - 2}]))), - <<"akk">> = iolist_to_binary(join(re:split("akk","[^k]{2,3}$",[]))), - <<"12345678.b.c.d">> = iolist_to_binary(join(re:split("12345678.b.c.d","^\\d{8,}\\@.+[^k]$",[trim]))), + 2}]))), + <<"akk">> = iolist_to_binary(join(re:split("akk","[^k]{2,3}$",[]))), + <<"12345678.b.c.d">> = iolist_to_binary(join(re:split("12345678.b.c.d","^\\d{8,}\\@.+[^k]$",[trim]))), <<"12345678.b.c.d">> = iolist_to_binary(join(re:split("12345678.b.c.d","^\\d{8,}\\@.+[^k]$",[{parts, - 2}]))), - <<"12345678.b.c.d">> = iolist_to_binary(join(re:split("12345678.b.c.d","^\\d{8,}\\@.+[^k]$",[]))), - <<"123456789.y.z">> = iolist_to_binary(join(re:split("123456789.y.z","^\\d{8,}\\@.+[^k]$",[trim]))), + 2}]))), + <<"12345678.b.c.d">> = iolist_to_binary(join(re:split("12345678.b.c.d","^\\d{8,}\\@.+[^k]$",[]))), + <<"123456789.y.z">> = iolist_to_binary(join(re:split("123456789.y.z","^\\d{8,}\\@.+[^k]$",[trim]))), <<"123456789.y.z">> = iolist_to_binary(join(re:split("123456789.y.z","^\\d{8,}\\@.+[^k]$",[{parts, - 2}]))), - <<"123456789.y.z">> = iolist_to_binary(join(re:split("123456789.y.z","^\\d{8,}\\@.+[^k]$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8,}\\@.+[^k]$",[trim]))), + 2}]))), + <<"123456789.y.z">> = iolist_to_binary(join(re:split("123456789.y.z","^\\d{8,}\\@.+[^k]$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8,}\\@.+[^k]$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8,}\\@.+[^k]$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8,}\\@.+[^k]$",[]))), - <<"12345678.y.uk">> = iolist_to_binary(join(re:split("12345678.y.uk","^\\d{8,}\\@.+[^k]$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\d{8,}\\@.+[^k]$",[]))), + <<"12345678.y.uk">> = iolist_to_binary(join(re:split("12345678.y.uk","^\\d{8,}\\@.+[^k]$",[trim]))), <<"12345678.y.uk">> = iolist_to_binary(join(re:split("12345678.y.uk","^\\d{8,}\\@.+[^k]$",[{parts, - 2}]))), - <<"12345678.y.uk">> = iolist_to_binary(join(re:split("12345678.y.uk","^\\d{8,}\\@.+[^k]$",[]))), - <<"1234567.b.c.d">> = iolist_to_binary(join(re:split("1234567.b.c.d","^\\d{8,}\\@.+[^k]$",[trim]))), + 2}]))), + <<"12345678.y.uk">> = iolist_to_binary(join(re:split("12345678.y.uk","^\\d{8,}\\@.+[^k]$",[]))), + <<"1234567.b.c.d">> = iolist_to_binary(join(re:split("1234567.b.c.d","^\\d{8,}\\@.+[^k]$",[trim]))), <<"1234567.b.c.d">> = iolist_to_binary(join(re:split("1234567.b.c.d","^\\d{8,}\\@.+[^k]$",[{parts, - 2}]))), - <<"1234567.b.c.d">> = iolist_to_binary(join(re:split("1234567.b.c.d","^\\d{8,}\\@.+[^k]$",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaaaaaa","(a)\\1{8,}",[trim]))), + 2}]))), + <<"1234567.b.c.d">> = iolist_to_binary(join(re:split("1234567.b.c.d","^\\d{8,}\\@.+[^k]$",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaaaaaa","(a)\\1{8,}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaaa","(a)\\1{8,}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaaa","(a)\\1{8,}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaaaaaaa","(a)\\1{8,}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaaa","(a)\\1{8,}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaaaaaaa","(a)\\1{8,}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","(a)\\1{8,}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","(a)\\1{8,}",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a)\\1{8,}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","(a)\\1{8,}",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a)\\1{8,}",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a)\\1{8,}",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a)\\1{8,}",[]))), - <<"aaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaa","(a)\\1{8,}",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a)\\1{8,}",[]))), + <<"aaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaa","(a)\\1{8,}",[trim]))), <<"aaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaa","(a)\\1{8,}",[{parts, - 2}]))), - <<"aaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaa","(a)\\1{8,}",[]))), + 2}]))), + <<"aaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaa","(a)\\1{8,}",[]))), ok. run7() -> - <<"aaaa">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[trim]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[trim]))), <<"aaaa:cd">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[{parts, - 2}]))), - <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[]))), - <<"aa:a">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[trim]))), + 2}]))), + <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[]))), + <<"aa:a">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[trim]))), <<"aa:abcd">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[{parts, - 2}]))), - <<"aa:a:::">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[]))), + 2}]))), + <<"aa:a:::">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[]))), <<"aaaa">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[caseless, - trim]))), + trim]))), <<"aaaa:cd">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[caseless, {parts, - 2}]))), - <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[caseless]))), + 2}]))), + <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^a]",[caseless]))), <<"aaAa">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[caseless, - trim]))), + trim]))), <<"aaAa:cd">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[caseless, {parts, - 2}]))), - <<"aaAa:::">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[caseless]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[trim]))), + 2}]))), + <<"aaAa:::">> = iolist_to_binary(join(re:split("aaAabcd","[^a]",[caseless]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[trim]))), <<"aaaa:cd">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[{parts, - 2}]))), - <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[]))), - <<"aa:a">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[trim]))), + 2}]))), + <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[]))), + <<"aa:a">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[trim]))), <<"aa:abcd">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[{parts, - 2}]))), - <<"aa:a:::">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[]))), + 2}]))), + <<"aa:a:::">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[]))), <<"aaaa">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[caseless, - trim]))), + trim]))), <<"aaaa:cd">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[caseless, {parts, - 2}]))), - <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[caseless]))), + 2}]))), + <<"aaaa:::">> = iolist_to_binary(join(re:split("aaaabcd","[^az]",[caseless]))), <<"aaAa">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[caseless, - trim]))), + trim]))), <<"aaAa:cd">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[caseless, {parts, - 2}]))), - <<"aaAa:::">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[caseless]))), - <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,6}?LL",[trim]))), + 2}]))), + <<"aaAa:::">> = iolist_to_binary(join(re:split("aaAabcd","[^az]",[caseless]))), + <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,6}?LL",[trim]))), <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,6}?LL",[{parts, - 2}]))), - <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,6}?LL",[]))), - <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,}?LL",[trim]))), + 2}]))), + <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,6}?LL",[]))), + <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,}?LL",[trim]))), <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,}?LL",[{parts, - 2}]))), - <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,}?LL",[]))), - <<"1:.23">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d[1-9]?)\\d+",[trim]))), + 2}]))), + <<"xxxxxxxxxxx:xxxxxxxxx">> = iolist_to_binary(join(re:split("xxxxxxxxxxxPSTAIREISLLxxxxxxxxx","P[^*]TAIRE[^*]{1,}?LL",[]))), + <<"1:.23">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d[1-9]?)\\d+",[trim]))), <<"1:.23:">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d[1-9]?)\\d+",[{parts, - 2}]))), - <<"1:.23:">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d[1-9]?)\\d+",[]))), - <<"1:.875">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d[1-9]?)\\d+",[trim]))), + 2}]))), + <<"1:.23:">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d[1-9]?)\\d+",[]))), + <<"1:.875">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d[1-9]?)\\d+",[trim]))), <<"1:.875:">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d[1-9]?)\\d+",[{parts, - 2}]))), - <<"1:.875:">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d[1-9]?)\\d+",[]))), - <<"1:.23">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d[1-9]?)\\d+",[trim]))), + 2}]))), + <<"1:.875:">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d[1-9]?)\\d+",[]))), + <<"1:.23">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d[1-9]?)\\d+",[trim]))), <<"1:.23:">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d[1-9]?)\\d+",[{parts, - 2}]))), - <<"1:.23:">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d[1-9]?)\\d+",[]))), - <<"1:.23::0003938">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), + 2}]))), + <<"1:.23:">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d[1-9]?)\\d+",[]))), + <<"1:.23::0003938">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), <<"1:.23::0003938">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[{parts, - 2}]))), - <<"1:.23::0003938">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), - <<"1:.875:5:000282">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), + 2}]))), + <<"1:.23::0003938">> = iolist_to_binary(join(re:split("1.230003938","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), + <<"1:.875:5:000282">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), <<"1:.875:5:000282">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[{parts, - 2}]))), - <<"1:.875:5:000282">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), + 2}]))), + <<"1:.875:5:000282">> = iolist_to_binary(join(re:split("1.875000282","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), - <<"1.235">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), + <<"1.235">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[trim]))), <<"1.235">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[{parts, - 2}]))), - <<"1.235">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), - <<"">> = iolist_to_binary(join(re:split("ab","a(?)b",[trim]))), + 2}]))), + <<"1.235">> = iolist_to_binary(join(re:split("1.235","(\\.\\d\\d((?=0)|\\d(?=\\d)))",[]))), + <<"">> = iolist_to_binary(join(re:split("ab","a(?)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab","a(?)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","a(?)b",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","a(?)b",[]))), <<"Food is on the :foo:table">> = iolist_to_binary(join(re:split("Food is on the foo table","\\b(foo)\\s+(\\w+)",[caseless, - trim]))), + trim]))), <<"Food is on the :foo:table:">> = iolist_to_binary(join(re:split("Food is on the foo table","\\b(foo)\\s+(\\w+)",[caseless, {parts, - 2}]))), - <<"Food is on the :foo:table:">> = iolist_to_binary(join(re:split("Food is on the foo table","\\b(foo)\\s+(\\w+)",[caseless]))), - <<"The :d is under the bar in the :n.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*)bar",[trim]))), + 2}]))), + <<"Food is on the :foo:table:">> = iolist_to_binary(join(re:split("Food is on the foo table","\\b(foo)\\s+(\\w+)",[caseless]))), + <<"The :d is under the bar in the :n.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*)bar",[trim]))), <<"The :d is under the bar in the :n.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*)bar",[{parts, - 2}]))), - <<"The :d is under the bar in the :n.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*)bar",[]))), - <<"The :d is under the : in the barn.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*?)bar",[trim]))), + 2}]))), + <<"The :d is under the bar in the :n.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*)bar",[]))), + <<"The :d is under the : in the barn.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*?)bar",[trim]))), <<"The :d is under the : in the barn.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*?)bar",[{parts, - 2}]))), - <<"The :d is under the : in the barn.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*?)bar",[]))), - <<":I have 2 numbers: 53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d*)",[trim]))), + 2}]))), + <<"The :d is under the : in the barn.">> = iolist_to_binary(join(re:split("The food is under the bar in the barn.","foo(.*?)bar",[]))), + <<":I have 2 numbers: 53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d*)",[trim]))), <<":I have 2 numbers: 53147::">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d*)",[{parts, - 2}]))), - <<":I have 2 numbers: 53147::">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d*)",[]))), - <<":I have 2 numbers: 5314:7">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)",[trim]))), + 2}]))), + <<":I have 2 numbers: 53147::">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d*)",[]))), + <<":I have 2 numbers: 5314:7">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)",[trim]))), <<":I have 2 numbers: 5314:7:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)",[{parts, - 2}]))), - <<":I have 2 numbers: 5314:7:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)",[]))), - <<":I::: :::h:::a:::v:::e::: :2:: :::n:::u:::m:::b:::e:::r:::s::::::: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d*)",[trim]))), + 2}]))), + <<":I have 2 numbers: 5314:7:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)",[]))), + <<":I::: :::h:::a:::v:::e::: :2:: :::n:::u:::m:::b:::e:::r:::s::::::: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d*)",[trim]))), <<":I:: have 2 numbers: 53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d*)",[{parts, - 2}]))), - <<":I::: :::h:::a:::v:::e::: :2:: :::n:::u:::m:::b:::e:::r:::s::::::: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d*)",[]))), - <<":I have :2:: numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)",[trim]))), + 2}]))), + <<":I::: :::h:::a:::v:::e::: :2:: :::n:::u:::m:::b:::e:::r:::s::::::: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d*)",[]))), + <<":I have :2:: numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)",[trim]))), <<":I have :2: numbers: 53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)",[{parts, - 2}]))), - <<":I have :2:: numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)",[]))), - <<":I have 2 numbers: 5314:7">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)$",[trim]))), + 2}]))), + <<":I have :2:: numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)",[]))), + <<":I have 2 numbers: 5314:7">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)$",[trim]))), <<":I have 2 numbers: 5314:7:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)$",[{parts, - 2}]))), - <<":I have 2 numbers: 5314:7:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)$",[]))), - <<":I have 2 numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)$",[trim]))), + 2}]))), + <<":I have 2 numbers: 5314:7:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)(\\d+)$",[]))), + <<":I have 2 numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)$",[trim]))), <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)$",[{parts, - 2}]))), - <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)$",[]))), - <<":I have 2 numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)\\b(\\d+)$",[trim]))), + 2}]))), + <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*?)(\\d+)$",[]))), + <<":I have 2 numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)\\b(\\d+)$",[trim]))), <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)\\b(\\d+)$",[{parts, - 2}]))), - <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)\\b(\\d+)$",[]))), + 2}]))), + <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*)\\b(\\d+)$",[]))), ok. run8() -> - <<":I have 2 numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*\\D)(\\d+)$",[trim]))), + <<":I have 2 numbers: :53147">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*\\D)(\\d+)$",[trim]))), <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*\\D)(\\d+)$",[{parts, - 2}]))), - <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*\\D)(\\d+)$",[]))), - <<":C123">> = iolist_to_binary(join(re:split("ABC123","^\\D*(?!123)",[trim]))), + 2}]))), + <<":I have 2 numbers: :53147:">> = iolist_to_binary(join(re:split("I have 2 numbers: 53147","(.*\\D)(\\d+)$",[]))), + <<":C123">> = iolist_to_binary(join(re:split("ABC123","^\\D*(?!123)",[trim]))), <<":C123">> = iolist_to_binary(join(re:split("ABC123","^\\D*(?!123)",[{parts, - 2}]))), - <<":C123">> = iolist_to_binary(join(re:split("ABC123","^\\D*(?!123)",[]))), - <<":ABC:445">> = iolist_to_binary(join(re:split("ABC445","^(\\D*)(?=\\d)(?!123)",[trim]))), + 2}]))), + <<":C123">> = iolist_to_binary(join(re:split("ABC123","^\\D*(?!123)",[]))), + <<":ABC:445">> = iolist_to_binary(join(re:split("ABC445","^(\\D*)(?=\\d)(?!123)",[trim]))), <<":ABC:445">> = iolist_to_binary(join(re:split("ABC445","^(\\D*)(?=\\d)(?!123)",[{parts, - 2}]))), - <<":ABC:445">> = iolist_to_binary(join(re:split("ABC445","^(\\D*)(?=\\d)(?!123)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[trim]))), + 2}]))), + <<":ABC:445">> = iolist_to_binary(join(re:split("ABC445","^(\\D*)(?=\\d)(?!123)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[]))), - <<"ABC123">> = iolist_to_binary(join(re:split("ABC123","^(\\D*)(?=\\d)(?!123)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\D*)(?=\\d)(?!123)",[]))), + <<"ABC123">> = iolist_to_binary(join(re:split("ABC123","^(\\D*)(?=\\d)(?!123)",[trim]))), <<"ABC123">> = iolist_to_binary(join(re:split("ABC123","^(\\D*)(?=\\d)(?!123)",[{parts, - 2}]))), - <<"ABC123">> = iolist_to_binary(join(re:split("ABC123","^(\\D*)(?=\\d)(?!123)",[]))), - <<":789">> = iolist_to_binary(join(re:split("W46]789","^[W-]46]",[trim]))), + 2}]))), + <<"ABC123">> = iolist_to_binary(join(re:split("ABC123","^(\\D*)(?=\\d)(?!123)",[]))), + <<":789">> = iolist_to_binary(join(re:split("W46]789","^[W-]46]",[trim]))), <<":789">> = iolist_to_binary(join(re:split("W46]789","^[W-]46]",[{parts, - 2}]))), - <<":789">> = iolist_to_binary(join(re:split("W46]789","^[W-]46]",[]))), - <<":789">> = iolist_to_binary(join(re:split("-46]789","^[W-]46]",[trim]))), + 2}]))), + <<":789">> = iolist_to_binary(join(re:split("W46]789","^[W-]46]",[]))), + <<":789">> = iolist_to_binary(join(re:split("-46]789","^[W-]46]",[trim]))), <<":789">> = iolist_to_binary(join(re:split("-46]789","^[W-]46]",[{parts, - 2}]))), - <<":789">> = iolist_to_binary(join(re:split("-46]789","^[W-]46]",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-]46]",[trim]))), + 2}]))), + <<":789">> = iolist_to_binary(join(re:split("-46]789","^[W-]46]",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-]46]",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-]46]",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-]46]",[]))), - <<"Wall">> = iolist_to_binary(join(re:split("Wall","^[W-]46]",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-]46]",[]))), + <<"Wall">> = iolist_to_binary(join(re:split("Wall","^[W-]46]",[trim]))), <<"Wall">> = iolist_to_binary(join(re:split("Wall","^[W-]46]",[{parts, - 2}]))), - <<"Wall">> = iolist_to_binary(join(re:split("Wall","^[W-]46]",[]))), - <<"Zebra">> = iolist_to_binary(join(re:split("Zebra","^[W-]46]",[trim]))), + 2}]))), + <<"Wall">> = iolist_to_binary(join(re:split("Wall","^[W-]46]",[]))), + <<"Zebra">> = iolist_to_binary(join(re:split("Zebra","^[W-]46]",[trim]))), <<"Zebra">> = iolist_to_binary(join(re:split("Zebra","^[W-]46]",[{parts, - 2}]))), - <<"Zebra">> = iolist_to_binary(join(re:split("Zebra","^[W-]46]",[]))), - <<"42">> = iolist_to_binary(join(re:split("42","^[W-]46]",[trim]))), + 2}]))), + <<"Zebra">> = iolist_to_binary(join(re:split("Zebra","^[W-]46]",[]))), + <<"42">> = iolist_to_binary(join(re:split("42","^[W-]46]",[trim]))), <<"42">> = iolist_to_binary(join(re:split("42","^[W-]46]",[{parts, - 2}]))), - <<"42">> = iolist_to_binary(join(re:split("42","^[W-]46]",[]))), - <<"[abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-]46]",[trim]))), + 2}]))), + <<"42">> = iolist_to_binary(join(re:split("42","^[W-]46]",[]))), + <<"[abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-]46]",[trim]))), <<"[abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-]46]",[{parts, - 2}]))), - <<"[abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-]46]",[]))), - <<"]abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-]46]",[trim]))), + 2}]))), + <<"[abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-]46]",[]))), + <<"]abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-]46]",[trim]))), <<"]abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-]46]",[{parts, - 2}]))), - <<"]abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-]46]",[]))), - <<":46]789">> = iolist_to_binary(join(re:split("W46]789","^[W-\\]46]",[trim]))), + 2}]))), + <<"]abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-]46]",[]))), + <<":46]789">> = iolist_to_binary(join(re:split("W46]789","^[W-\\]46]",[trim]))), <<":46]789">> = iolist_to_binary(join(re:split("W46]789","^[W-\\]46]",[{parts, - 2}]))), - <<":46]789">> = iolist_to_binary(join(re:split("W46]789","^[W-\\]46]",[]))), - <<":all">> = iolist_to_binary(join(re:split("Wall","^[W-\\]46]",[trim]))), + 2}]))), + <<":46]789">> = iolist_to_binary(join(re:split("W46]789","^[W-\\]46]",[]))), + <<":all">> = iolist_to_binary(join(re:split("Wall","^[W-\\]46]",[trim]))), <<":all">> = iolist_to_binary(join(re:split("Wall","^[W-\\]46]",[{parts, - 2}]))), - <<":all">> = iolist_to_binary(join(re:split("Wall","^[W-\\]46]",[]))), - <<":ebra">> = iolist_to_binary(join(re:split("Zebra","^[W-\\]46]",[trim]))), + 2}]))), + <<":all">> = iolist_to_binary(join(re:split("Wall","^[W-\\]46]",[]))), + <<":ebra">> = iolist_to_binary(join(re:split("Zebra","^[W-\\]46]",[trim]))), <<":ebra">> = iolist_to_binary(join(re:split("Zebra","^[W-\\]46]",[{parts, - 2}]))), - <<":ebra">> = iolist_to_binary(join(re:split("Zebra","^[W-\\]46]",[]))), - <<":ylophone">> = iolist_to_binary(join(re:split("Xylophone","^[W-\\]46]",[trim]))), + 2}]))), + <<":ebra">> = iolist_to_binary(join(re:split("Zebra","^[W-\\]46]",[]))), + <<":ylophone">> = iolist_to_binary(join(re:split("Xylophone","^[W-\\]46]",[trim]))), <<":ylophone">> = iolist_to_binary(join(re:split("Xylophone","^[W-\\]46]",[{parts, - 2}]))), - <<":ylophone">> = iolist_to_binary(join(re:split("Xylophone","^[W-\\]46]",[]))), - <<":2">> = iolist_to_binary(join(re:split("42","^[W-\\]46]",[trim]))), + 2}]))), + <<":ylophone">> = iolist_to_binary(join(re:split("Xylophone","^[W-\\]46]",[]))), + <<":2">> = iolist_to_binary(join(re:split("42","^[W-\\]46]",[trim]))), <<":2">> = iolist_to_binary(join(re:split("42","^[W-\\]46]",[{parts, - 2}]))), - <<":2">> = iolist_to_binary(join(re:split("42","^[W-\\]46]",[]))), - <<":abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-\\]46]",[trim]))), + 2}]))), + <<":2">> = iolist_to_binary(join(re:split("42","^[W-\\]46]",[]))), + <<":abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-\\]46]",[trim]))), <<":abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-\\]46]",[{parts, - 2}]))), - <<":abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-\\]46]",[]))), - <<":abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-\\]46]",[trim]))), + 2}]))), + <<":abcd]">> = iolist_to_binary(join(re:split("[abcd]","^[W-\\]46]",[]))), + <<":abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-\\]46]",[trim]))), <<":abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-\\]46]",[{parts, - 2}]))), - <<":abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-\\]46]",[]))), - <<":backslash">> = iolist_to_binary(join(re:split("\\backslash","^[W-\\]46]",[trim]))), + 2}]))), + <<":abcd[">> = iolist_to_binary(join(re:split("]abcd[","^[W-\\]46]",[]))), + <<":backslash">> = iolist_to_binary(join(re:split("\\backslash","^[W-\\]46]",[trim]))), <<":backslash">> = iolist_to_binary(join(re:split("\\backslash","^[W-\\]46]",[{parts, - 2}]))), - <<":backslash">> = iolist_to_binary(join(re:split("\\backslash","^[W-\\]46]",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-\\]46]",[trim]))), + 2}]))), + <<":backslash">> = iolist_to_binary(join(re:split("\\backslash","^[W-\\]46]",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-\\]46]",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-\\]46]",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-\\]46]",[]))), - <<"-46]789">> = iolist_to_binary(join(re:split("-46]789","^[W-\\]46]",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[W-\\]46]",[]))), + <<"-46]789">> = iolist_to_binary(join(re:split("-46]789","^[W-\\]46]",[trim]))), <<"-46]789">> = iolist_to_binary(join(re:split("-46]789","^[W-\\]46]",[{parts, - 2}]))), - <<"-46]789">> = iolist_to_binary(join(re:split("-46]789","^[W-\\]46]",[]))), - <<"well">> = iolist_to_binary(join(re:split("well","^[W-\\]46]",[trim]))), + 2}]))), + <<"-46]789">> = iolist_to_binary(join(re:split("-46]789","^[W-\\]46]",[]))), + <<"well">> = iolist_to_binary(join(re:split("well","^[W-\\]46]",[trim]))), <<"well">> = iolist_to_binary(join(re:split("well","^[W-\\]46]",[{parts, - 2}]))), - <<"well">> = iolist_to_binary(join(re:split("well","^[W-\\]46]",[]))), - <<"">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[trim]))), + 2}]))), + <<"well">> = iolist_to_binary(join(re:split("well","^[W-\\]46]",[]))), + <<"">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[trim]))), <<":">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[trim]))), <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[{parts, - 2}]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?:[a-zA-Z0-9]+ ){0,300}otherword",[trim]))), + 2}]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?:[a-zA-Z0-9]+ ){0,10}otherword",[]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?:[a-zA-Z0-9]+ ){0,300}otherword",[trim]))), <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?:[a-zA-Z0-9]+ ){0,300}otherword",[{parts, - 2}]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?:[a-zA-Z0-9]+ ){0,300}otherword",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,0}",[trim]))), + 2}]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?:[a-zA-Z0-9]+ ){0,300}otherword",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,0}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,0}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,0}",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(a){0,0}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,0}",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(a){0,0}",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^(a){0,0}",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(a){0,0}",[]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^(a){0,0}",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(a){0,0}",[]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^(a){0,0}",[trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","^(a){0,0}",[{parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^(a){0,0}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,1}",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^(a){0,0}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,1}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,1}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,1}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,1}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,1}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,1}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,1}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,1}",[]))), - <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){0,1}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,1}",[]))), + <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){0,1}",[trim]))), <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){0,1}",[{parts, - 2}]))), - <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){0,1}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,2}",[trim]))), + 2}]))), + <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){0,1}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,2}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,2}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,2}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,2}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,2}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,2}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,2}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,2}",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,2}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,2}",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,2}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,2}",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,2}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,3}",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,2}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,3}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,3}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,3}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,3}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,3}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,3}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,3}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,3}",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,3}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,3}",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,3}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,3}",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,3}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){0,3}",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,3}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){0,3}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){0,3}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){0,3}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){0,3}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){0,}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,}",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){0,}",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,}",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){0,}",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){0,}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){0,}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){0,}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){0,}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){0,}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){0,}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){0,}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){0,}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){0,}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,1}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){0,}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,1}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,1}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,1}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,1}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,1}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,1}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,1}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,1}",[]))), - <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){1,1}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,1}",[]))), + <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){1,1}",[trim]))), <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){1,1}",[{parts, - 2}]))), - <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){1,1}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,2}",[trim]))), + 2}]))), + <<":a:ab">> = iolist_to_binary(join(re:split("aab","^(a){1,1}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,2}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,2}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,2}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,2}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,2}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,2}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,2}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,2}",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,2}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,2}",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,2}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,2}",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,2}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,3}",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,2}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,3}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,3}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,3}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,3}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,3}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,3}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,3}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,3}",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,3}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,3}",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,3}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,3}",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,3}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){1,3}",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,3}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){1,3}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){1,3}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){1,3}",[]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){1,3}",[]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,}",[trim]))), <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,}",[{parts, - 2}]))), - <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,}",[]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,}",[trim]))), + 2}]))), + <<"bcd">> = iolist_to_binary(join(re:split("bcd","^(a){1,}",[]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,}",[trim]))), <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,}",[{parts, - 2}]))), - <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,}",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,}",[trim]))), + 2}]))), + <<":a:bc">> = iolist_to_binary(join(re:split("abc","^(a){1,}",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,}",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){1,}",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("aab","^(a){1,}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaa","^(a){1,}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){1,}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){1,}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){1,}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaa","^(a){1,}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){1,}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){1,}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){1,}",[]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a){1,}",[]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[trim]))), +no",".*\\.gif",[trim]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[{parts,2}]))), +no",".*\\.gif",[{parts,2}]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[]))), +no",".*\\.gif",[]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".{0,}\\.gif",[trim]))), +no",".{0,}\\.gif",[trim]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".{0,}\\.gif",[{parts,2}]))), +no",".{0,}\\.gif",[{parts,2}]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".{0,}\\.gif",[]))), +no",".{0,}\\.gif",[]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[multiline,trim]))), +no",".*\\.gif",[multiline,trim]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[multiline,{parts,2}]))), +no",".*\\.gif",[multiline,{parts,2}]))), <<"borfle : no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[multiline]))), +no",".*\\.gif",[multiline]))), ok. run9() -> <<": no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[dotall,trim]))), +no",".*\\.gif",[dotall,trim]))), <<": no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[dotall,{parts,2}]))), +no",".*\\.gif",[dotall,{parts,2}]))), <<": no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[dotall]))), +no",".*\\.gif",[dotall]))), <<": no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[multiline,dotall,trim]))), +no",".*\\.gif",[multiline,dotall,trim]))), <<": no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[multiline,dotall,{parts,2}]))), +no",".*\\.gif",[multiline,dotall,{parts,2}]))), <<": no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*\\.gif",[multiline,dotall]))), +no",".*\\.gif",[multiline,dotall]))), <<"borfle bib.gif ">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[trim]))), +no",".*$",[trim]))), <<"borfle bib.gif :">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[{parts,2}]))), +no",".*$",[{parts,2}]))), <<"borfle bib.gif :">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[]))), +no",".*$",[]))), <<": : ">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,trim]))), +no",".*$",[multiline,trim]))), <<": bib.gif no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,{parts,2}]))), +no",".*$",[multiline,{parts,2}]))), <<": : :">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline]))), +no",".*$",[multiline]))), <<"">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[dotall,trim]))), +no",".*$",[dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[dotall,{parts,2}]))), +no",".*$",[dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[dotall]))), +no",".*$",[dotall]))), <<"">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,dotall,trim]))), +no",".*$",[multiline,dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,dotall,{parts,2}]))), +no",".*$",[multiline,dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,dotall]))), +no",".*$",[multiline,dotall]))), <<"borfle bib.gif ">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[trim]))), +no",".*$",[trim]))), <<"borfle bib.gif :">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[{parts,2}]))), +no",".*$",[{parts,2}]))), <<"borfle bib.gif :">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[]))), +no",".*$",[]))), <<": : ">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,trim]))), +no",".*$",[multiline,trim]))), <<": bib.gif no">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,{parts,2}]))), +no",".*$",[multiline,{parts,2}]))), <<": : :">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline]))), +no",".*$",[multiline]))), <<"">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[dotall,trim]))), +no",".*$",[dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[dotall,{parts,2}]))), +no",".*$",[dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[dotall]))), +no",".*$",[dotall]))), <<"">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,dotall,trim]))), +no",".*$",[multiline,dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,dotall,{parts,2}]))), +no",".*$",[multiline,dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("borfle bib.gif -no",".*$",[multiline,dotall]))), +no",".*$",[multiline,dotall]))), <<"abcde :1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[trim]))), +1234Xyz","(.*X|^B)",[trim]))), <<"abcde :1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[{parts,2}]))), +1234Xyz","(.*X|^B)",[{parts,2}]))), <<"abcde :1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[]))), - <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[trim]))), +1234Xyz","(.*X|^B)",[]))), + <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[trim]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[{parts, - 2}]))), - <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[trim]))), + 2}]))), + <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[trim]))), +Bar","(.*X|^B)",[trim]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[{parts,2}]))), +Bar","(.*X|^B)",[{parts,2}]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[]))), +Bar","(.*X|^B)",[]))), <<"abcde :1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[multiline,trim]))), +1234Xyz","(.*X|^B)",[multiline,trim]))), <<"abcde :1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[multiline,{parts,2}]))), +1234Xyz","(.*X|^B)",[multiline,{parts,2}]))), <<"abcde :1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[multiline]))), +1234Xyz","(.*X|^B)",[multiline]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline, - trim]))), + trim]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline, {parts, - 2}]))), - <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline]))), + 2}]))), + <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline]))), <<"abcde :B:ar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[multiline,trim]))), +Bar","(.*X|^B)",[multiline,trim]))), <<"abcde :B:ar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[multiline,{parts,2}]))), +Bar","(.*X|^B)",[multiline,{parts,2}]))), <<"abcde :B:ar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[multiline]))), +Bar","(.*X|^B)",[multiline]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[dotall,trim]))), +1234Xyz","(.*X|^B)",[dotall,trim]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[dotall,{parts,2}]))), +1234Xyz","(.*X|^B)",[dotall,{parts,2}]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[dotall]))), +1234Xyz","(.*X|^B)",[dotall]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[dotall, - trim]))), + trim]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[dotall, {parts, - 2}]))), - <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[dotall]))), + 2}]))), + <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[dotall]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[dotall, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[dotall, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[dotall]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(.*X|^B)",[dotall]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[dotall,trim]))), +Bar","(.*X|^B)",[dotall,trim]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[dotall,{parts,2}]))), +Bar","(.*X|^B)",[dotall,{parts,2}]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[dotall]))), +Bar","(.*X|^B)",[dotall]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[multiline,dotall,trim]))), +1234Xyz","(.*X|^B)",[multiline,dotall,trim]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[multiline,dotall,{parts,2}]))), +1234Xyz","(.*X|^B)",[multiline,dotall,{parts,2}]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(.*X|^B)",[multiline,dotall]))), +1234Xyz","(.*X|^B)",[multiline,dotall]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline, dotall, - trim]))), + trim]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline, dotall, {parts, - 2}]))), + 2}]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(.*X|^B)",[multiline, - dotall]))), + dotall]))), <<"abcde :B:ar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[multiline,dotall,trim]))), +Bar","(.*X|^B)",[multiline,dotall,trim]))), <<"abcde :B:ar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[multiline,dotall,{parts,2}]))), +Bar","(.*X|^B)",[multiline,dotall,{parts,2}]))), <<"abcde :B:ar">> = iolist_to_binary(join(re:split("abcde -Bar","(.*X|^B)",[multiline,dotall]))), +Bar","(.*X|^B)",[multiline,dotall]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(?s)(.*X|^B)",[trim]))), +1234Xyz","(?s)(.*X|^B)",[trim]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(?s)(.*X|^B)",[{parts,2}]))), +1234Xyz","(?s)(.*X|^B)",[{parts,2}]))), <<":abcde 1234X:yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(?s)(.*X|^B)",[]))), - <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s)(.*X|^B)",[trim]))), +1234Xyz","(?s)(.*X|^B)",[]))), + <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s)(.*X|^B)",[trim]))), <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s)(.*X|^B)",[{parts, - 2}]))), - <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s)(.*X|^B)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s)(.*X|^B)",[trim]))), + 2}]))), + <<":B:arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s)(.*X|^B)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s)(.*X|^B)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s)(.*X|^B)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s)(.*X|^B)",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s)(.*X|^B)",[]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(?s)(.*X|^B)",[trim]))), +Bar","(?s)(.*X|^B)",[trim]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(?s)(.*X|^B)",[{parts,2}]))), +Bar","(?s)(.*X|^B)",[{parts,2}]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(?s)(.*X|^B)",[]))), +Bar","(?s)(.*X|^B)",[]))), <<":yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(?s:.*X|^B)",[trim]))), +1234Xyz","(?s:.*X|^B)",[trim]))), <<":yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(?s:.*X|^B)",[{parts,2}]))), +1234Xyz","(?s:.*X|^B)",[{parts,2}]))), <<":yz">> = iolist_to_binary(join(re:split("abcde -1234Xyz","(?s:.*X|^B)",[]))), - <<":arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s:.*X|^B)",[trim]))), +1234Xyz","(?s:.*X|^B)",[]))), + <<":arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s:.*X|^B)",[trim]))), <<":arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s:.*X|^B)",[{parts, - 2}]))), - <<":arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s:.*X|^B)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s:.*X|^B)",[trim]))), + 2}]))), + <<":arFoo">> = iolist_to_binary(join(re:split("BarFoo","(?s:.*X|^B)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s:.*X|^B)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s:.*X|^B)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s:.*X|^B)",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s:.*X|^B)",[]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(?s:.*X|^B)",[trim]))), +Bar","(?s:.*X|^B)",[trim]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(?s:.*X|^B)",[{parts,2}]))), +Bar","(?s:.*X|^B)",[{parts,2}]))), <<"abcde Bar">> = iolist_to_binary(join(re:split("abcde -Bar","(?s:.*X|^B)",[]))), - <<"**** Failers">> = iolist_to_binary(join(re:split("**** Failers","^.*B",[trim]))), +Bar","(?s:.*X|^B)",[]))), + <<"**** Failers">> = iolist_to_binary(join(re:split("**** Failers","^.*B",[trim]))), <<"**** Failers">> = iolist_to_binary(join(re:split("**** Failers","^.*B",[{parts, - 2}]))), - <<"**** Failers">> = iolist_to_binary(join(re:split("**** Failers","^.*B",[]))), + 2}]))), + <<"**** Failers">> = iolist_to_binary(join(re:split("**** Failers","^.*B",[]))), <<"abc B">> = iolist_to_binary(join(re:split("abc -B","^.*B",[trim]))), +B","^.*B",[trim]))), <<"abc B">> = iolist_to_binary(join(re:split("abc -B","^.*B",[{parts,2}]))), +B","^.*B",[{parts,2}]))), <<"abc B">> = iolist_to_binary(join(re:split("abc -B","^.*B",[]))), +B","^.*B",[]))), <<"">> = iolist_to_binary(join(re:split("abc -B","(?s)^.*B",[trim]))), +B","(?s)^.*B",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc -B","(?s)^.*B",[{parts,2}]))), +B","(?s)^.*B",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc -B","(?s)^.*B",[]))), +B","(?s)^.*B",[]))), <<"abc ">> = iolist_to_binary(join(re:split("abc -B","(?m)^.*B",[trim]))), +B","(?m)^.*B",[trim]))), <<"abc :">> = iolist_to_binary(join(re:split("abc -B","(?m)^.*B",[{parts,2}]))), +B","(?m)^.*B",[{parts,2}]))), <<"abc :">> = iolist_to_binary(join(re:split("abc -B","(?m)^.*B",[]))), +B","(?m)^.*B",[]))), <<"">> = iolist_to_binary(join(re:split("abc -B","(?ms)^.*B",[trim]))), +B","(?ms)^.*B",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc -B","(?ms)^.*B",[{parts,2}]))), +B","(?ms)^.*B",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc -B","(?ms)^.*B",[]))), +B","(?ms)^.*B",[]))), ok. run10() -> <<"abc ">> = iolist_to_binary(join(re:split("abc -B","(?ms)^B",[trim]))), +B","(?ms)^B",[trim]))), <<"abc :">> = iolist_to_binary(join(re:split("abc -B","(?ms)^B",[{parts,2}]))), +B","(?ms)^B",[{parts,2}]))), <<"abc :">> = iolist_to_binary(join(re:split("abc -B","(?ms)^B",[]))), - <<"">> = iolist_to_binary(join(re:split("B","(?s)B$",[trim]))), +B","(?ms)^B",[]))), + <<"">> = iolist_to_binary(join(re:split("B","(?s)B$",[trim]))), <<":">> = iolist_to_binary(join(re:split("B","(?s)B$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("B","(?s)B$",[]))), - <<"">> = iolist_to_binary(join(re:split("123456654321","^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("B","(?s)B$",[]))), + <<"">> = iolist_to_binary(join(re:split("123456654321","^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",[trim]))), <<":">> = iolist_to_binary(join(re:split("123456654321","^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("123456654321","^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",[]))), - <<"">> = iolist_to_binary(join(re:split("123456654321","^\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("123456654321","^[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]",[]))), + <<"">> = iolist_to_binary(join(re:split("123456654321","^\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d",[trim]))), <<":">> = iolist_to_binary(join(re:split("123456654321","^\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("123456654321","^\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d",[]))), - <<"">> = iolist_to_binary(join(re:split("123456654321","^[\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("123456654321","^\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d\\d",[]))), + <<"">> = iolist_to_binary(join(re:split("123456654321","^[\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d]",[trim]))), <<":">> = iolist_to_binary(join(re:split("123456654321","^[\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("123456654321","^[\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d]",[]))), - <<"">> = iolist_to_binary(join(re:split("abcabcabcabc","^[abc]{12}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("123456654321","^[\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d][\\d]",[]))), + <<"">> = iolist_to_binary(join(re:split("abcabcabcabc","^[abc]{12}",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcabcabcabc","^[abc]{12}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcabcabcabc","^[abc]{12}",[]))), - <<"">> = iolist_to_binary(join(re:split("abcabcabcabc","^[a-c]{12}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcabcabcabc","^[abc]{12}",[]))), + <<"">> = iolist_to_binary(join(re:split("abcabcabcabc","^[a-c]{12}",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcabcabcabc","^[a-c]{12}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcabcabcabc","^[a-c]{12}",[]))), - <<":c">> = iolist_to_binary(join(re:split("abcabcabcabc","^(a|b|c){12}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcabcabcabc","^[a-c]{12}",[]))), + <<":c">> = iolist_to_binary(join(re:split("abcabcabcabc","^(a|b|c){12}",[trim]))), <<":c:">> = iolist_to_binary(join(re:split("abcabcabcabc","^(a|b|c){12}",[{parts, - 2}]))), - <<":c:">> = iolist_to_binary(join(re:split("abcabcabcabc","^(a|b|c){12}",[]))), - <<"">> = iolist_to_binary(join(re:split("n","^[abcdefghijklmnopqrstuvwxy0123456789]",[trim]))), + 2}]))), + <<":c:">> = iolist_to_binary(join(re:split("abcabcabcabc","^(a|b|c){12}",[]))), + <<"">> = iolist_to_binary(join(re:split("n","^[abcdefghijklmnopqrstuvwxy0123456789]",[trim]))), <<":">> = iolist_to_binary(join(re:split("n","^[abcdefghijklmnopqrstuvwxy0123456789]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("n","^[abcdefghijklmnopqrstuvwxy0123456789]",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[abcdefghijklmnopqrstuvwxy0123456789]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("n","^[abcdefghijklmnopqrstuvwxy0123456789]",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[abcdefghijklmnopqrstuvwxy0123456789]",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[abcdefghijklmnopqrstuvwxy0123456789]",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[abcdefghijklmnopqrstuvwxy0123456789]",[]))), - <<"z">> = iolist_to_binary(join(re:split("z","^[abcdefghijklmnopqrstuvwxy0123456789]",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[abcdefghijklmnopqrstuvwxy0123456789]",[]))), + <<"z">> = iolist_to_binary(join(re:split("z","^[abcdefghijklmnopqrstuvwxy0123456789]",[trim]))), <<"z">> = iolist_to_binary(join(re:split("z","^[abcdefghijklmnopqrstuvwxy0123456789]",[{parts, - 2}]))), - <<"z">> = iolist_to_binary(join(re:split("z","^[abcdefghijklmnopqrstuvwxy0123456789]",[]))), - <<"">> = iolist_to_binary(join(re:split("abcd","abcde{0,0}",[trim]))), + 2}]))), + <<"z">> = iolist_to_binary(join(re:split("z","^[abcdefghijklmnopqrstuvwxy0123456789]",[]))), + <<"">> = iolist_to_binary(join(re:split("abcd","abcde{0,0}",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcd","abcde{0,0}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcd","abcde{0,0}",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abcde{0,0}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcd","abcde{0,0}",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abcde{0,0}",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abcde{0,0}",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abcde{0,0}",[]))), - <<"abce">> = iolist_to_binary(join(re:split("abce","abcde{0,0}",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abcde{0,0}",[]))), + <<"abce">> = iolist_to_binary(join(re:split("abce","abcde{0,0}",[trim]))), <<"abce">> = iolist_to_binary(join(re:split("abce","abcde{0,0}",[{parts, - 2}]))), - <<"abce">> = iolist_to_binary(join(re:split("abce","abcde{0,0}",[]))), - <<"">> = iolist_to_binary(join(re:split("abe","ab[cd]{0,0}e",[trim]))), + 2}]))), + <<"abce">> = iolist_to_binary(join(re:split("abce","abcde{0,0}",[]))), + <<"">> = iolist_to_binary(join(re:split("abe","ab[cd]{0,0}e",[trim]))), <<":">> = iolist_to_binary(join(re:split("abe","ab[cd]{0,0}e",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abe","ab[cd]{0,0}e",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab[cd]{0,0}e",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abe","ab[cd]{0,0}e",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab[cd]{0,0}e",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab[cd]{0,0}e",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab[cd]{0,0}e",[]))), - <<"abcde">> = iolist_to_binary(join(re:split("abcde","ab[cd]{0,0}e",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab[cd]{0,0}e",[]))), + <<"abcde">> = iolist_to_binary(join(re:split("abcde","ab[cd]{0,0}e",[trim]))), <<"abcde">> = iolist_to_binary(join(re:split("abcde","ab[cd]{0,0}e",[{parts, - 2}]))), - <<"abcde">> = iolist_to_binary(join(re:split("abcde","ab[cd]{0,0}e",[]))), - <<"">> = iolist_to_binary(join(re:split("abd","ab(c){0,0}d",[trim]))), + 2}]))), + <<"abcde">> = iolist_to_binary(join(re:split("abcde","ab[cd]{0,0}e",[]))), + <<"">> = iolist_to_binary(join(re:split("abd","ab(c){0,0}d",[trim]))), <<"::">> = iolist_to_binary(join(re:split("abd","ab(c){0,0}d",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abd","ab(c){0,0}d",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab(c){0,0}d",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abd","ab(c){0,0}d",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab(c){0,0}d",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab(c){0,0}d",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab(c){0,0}d",[]))), - <<"abcd">> = iolist_to_binary(join(re:split("abcd","ab(c){0,0}d",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab(c){0,0}d",[]))), + <<"abcd">> = iolist_to_binary(join(re:split("abcd","ab(c){0,0}d",[trim]))), <<"abcd">> = iolist_to_binary(join(re:split("abcd","ab(c){0,0}d",[{parts, - 2}]))), - <<"abcd">> = iolist_to_binary(join(re:split("abcd","ab(c){0,0}d",[]))), - <<"">> = iolist_to_binary(join(re:split("a","a(b*)",[trim]))), + 2}]))), + <<"abcd">> = iolist_to_binary(join(re:split("abcd","ab(c){0,0}d",[]))), + <<"">> = iolist_to_binary(join(re:split("a","a(b*)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","a(b*)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","a(b*)",[]))), - <<":b">> = iolist_to_binary(join(re:split("ab","a(b*)",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","a(b*)",[]))), + <<":b">> = iolist_to_binary(join(re:split("ab","a(b*)",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("ab","a(b*)",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("ab","a(b*)",[]))), - <<":bbbb">> = iolist_to_binary(join(re:split("abbbb","a(b*)",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("ab","a(b*)",[]))), + <<":bbbb">> = iolist_to_binary(join(re:split("abbbb","a(b*)",[trim]))), <<":bbbb:">> = iolist_to_binary(join(re:split("abbbb","a(b*)",[{parts, - 2}]))), - <<":bbbb:">> = iolist_to_binary(join(re:split("abbbb","a(b*)",[]))), - <<"*** F::ilers">> = iolist_to_binary(join(re:split("*** Failers","a(b*)",[trim]))), + 2}]))), + <<":bbbb:">> = iolist_to_binary(join(re:split("abbbb","a(b*)",[]))), + <<"*** F::ilers">> = iolist_to_binary(join(re:split("*** Failers","a(b*)",[trim]))), <<"*** F::ilers">> = iolist_to_binary(join(re:split("*** Failers","a(b*)",[{parts, - 2}]))), - <<"*** F::ilers">> = iolist_to_binary(join(re:split("*** Failers","a(b*)",[]))), - <<"bbbbb">> = iolist_to_binary(join(re:split("bbbbb","a(b*)",[trim]))), + 2}]))), + <<"*** F::ilers">> = iolist_to_binary(join(re:split("*** Failers","a(b*)",[]))), + <<"bbbbb">> = iolist_to_binary(join(re:split("bbbbb","a(b*)",[trim]))), <<"bbbbb">> = iolist_to_binary(join(re:split("bbbbb","a(b*)",[{parts, - 2}]))), - <<"bbbbb">> = iolist_to_binary(join(re:split("bbbbb","a(b*)",[]))), - <<"">> = iolist_to_binary(join(re:split("abe","ab\\d{0}e",[trim]))), + 2}]))), + <<"bbbbb">> = iolist_to_binary(join(re:split("bbbbb","a(b*)",[]))), + <<"">> = iolist_to_binary(join(re:split("abe","ab\\d{0}e",[trim]))), <<":">> = iolist_to_binary(join(re:split("abe","ab\\d{0}e",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abe","ab\\d{0}e",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab\\d{0}e",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abe","ab\\d{0}e",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab\\d{0}e",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab\\d{0}e",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab\\d{0}e",[]))), - <<"ab1e">> = iolist_to_binary(join(re:split("ab1e","ab\\d{0}e",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab\\d{0}e",[]))), + <<"ab1e">> = iolist_to_binary(join(re:split("ab1e","ab\\d{0}e",[trim]))), <<"ab1e">> = iolist_to_binary(join(re:split("ab1e","ab\\d{0}e",[{parts, - 2}]))), - <<"ab1e">> = iolist_to_binary(join(re:split("ab1e","ab\\d{0}e",[]))), - <<"the :quick: brown fox">> = iolist_to_binary(join(re:split("the \"quick\" brown fox","\"([^\\\\\"]+|\\\\.)*\"",[trim]))), + 2}]))), + <<"ab1e">> = iolist_to_binary(join(re:split("ab1e","ab\\d{0}e",[]))), + <<"the :quick: brown fox">> = iolist_to_binary(join(re:split("the \"quick\" brown fox","\"([^\\\\\"]+|\\\\.)*\"",[trim]))), <<"the :quick: brown fox">> = iolist_to_binary(join(re:split("the \"quick\" brown fox","\"([^\\\\\"]+|\\\\.)*\"",[{parts, - 2}]))), - <<"the :quick: brown fox">> = iolist_to_binary(join(re:split("the \"quick\" brown fox","\"([^\\\\\"]+|\\\\.)*\"",[]))), - <<": brown fox">> = iolist_to_binary(join(re:split("\"the \\\"quick\\\" brown fox\"","\"([^\\\\\"]+|\\\\.)*\"",[trim]))), + 2}]))), + <<"the :quick: brown fox">> = iolist_to_binary(join(re:split("the \"quick\" brown fox","\"([^\\\\\"]+|\\\\.)*\"",[]))), + <<": brown fox">> = iolist_to_binary(join(re:split("\"the \\\"quick\\\" brown fox\"","\"([^\\\\\"]+|\\\\.)*\"",[trim]))), <<": brown fox:">> = iolist_to_binary(join(re:split("\"the \\\"quick\\\" brown fox\"","\"([^\\\\\"]+|\\\\.)*\"",[{parts, - 2}]))), - <<": brown fox:">> = iolist_to_binary(join(re:split("\"the \\\"quick\\\" brown fox\"","\"([^\\\\\"]+|\\\\.)*\"",[]))), - <<"a:b:c">> = iolist_to_binary(join(re:split("abc","",[trim]))), + 2}]))), + <<": brown fox:">> = iolist_to_binary(join(re:split("\"the \\\"quick\\\" brown fox\"","\"([^\\\\\"]+|\\\\.)*\"",[]))), + <<"a:b:c">> = iolist_to_binary(join(re:split("abc","",[trim]))), <<"a:bc">> = iolist_to_binary(join(re:split("abc","",[{parts, - 2}]))), - <<"a:b:c:">> = iolist_to_binary(join(re:split("abc","",[]))), - <<"">> = iolist_to_binary(join(re:split("acb","a[^a]b",[trim]))), + 2}]))), + <<"a:b:c:">> = iolist_to_binary(join(re:split("abc","",[]))), + <<"">> = iolist_to_binary(join(re:split("acb","a[^a]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("acb","a[^a]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("acb","a[^a]b",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("acb","a[^a]b",[]))), <<"">> = iolist_to_binary(join(re:split("a -b","a[^a]b",[trim]))), +b","a[^a]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a -b","a[^a]b",[{parts,2}]))), +b","a[^a]b",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("a -b","a[^a]b",[]))), - <<"">> = iolist_to_binary(join(re:split("acb","a.b",[trim]))), +b","a[^a]b",[]))), + <<"">> = iolist_to_binary(join(re:split("acb","a.b",[trim]))), <<":">> = iolist_to_binary(join(re:split("acb","a.b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("acb","a.b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("acb","a.b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.b",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.b",[]))), <<"a b">> = iolist_to_binary(join(re:split("a -b","a.b",[trim]))), +b","a.b",[trim]))), <<"a b">> = iolist_to_binary(join(re:split("a -b","a.b",[{parts,2}]))), +b","a.b",[{parts,2}]))), <<"a b">> = iolist_to_binary(join(re:split("a -b","a.b",[]))), +b","a.b",[]))), <<"">> = iolist_to_binary(join(re:split("acb","a[^a]b",[dotall, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("acb","a[^a]b",[dotall, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("acb","a[^a]b",[dotall]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("acb","a[^a]b",[dotall]))), <<"">> = iolist_to_binary(join(re:split("a -b","a[^a]b",[dotall,trim]))), +b","a[^a]b",[dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("a -b","a[^a]b",[dotall,{parts,2}]))), +b","a[^a]b",[dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("a -b","a[^a]b",[dotall]))), +b","a[^a]b",[dotall]))), ok. run11() -> <<"">> = iolist_to_binary(join(re:split("acb","a.b",[dotall, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("acb","a.b",[dotall, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("acb","a.b",[dotall]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("acb","a.b",[dotall]))), <<"">> = iolist_to_binary(join(re:split("a -b","a.b",[dotall,trim]))), +b","a.b",[dotall,trim]))), <<":">> = iolist_to_binary(join(re:split("a -b","a.b",[dotall,{parts,2}]))), +b","a.b",[dotall,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("a -b","a.b",[dotall]))), - <<":a">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[trim]))), +b","a.b",[dotall]))), + <<":a">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbbac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbbac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbbbac","^(b+?|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbbac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbbbac","^(b+?|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbbbac","^(b+?|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbbbac","^(b+?|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbbbac","^(b+?|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bac","^(b+|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbac","^(b+|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbac","^(b+|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbac","^(b+|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbac","^(b+|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbac","^(b+|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbbac","^(b+|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbac","^(b+|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbbac","^(b+|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbbac","^(b+|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbbac","^(b+|a){1,2}?c",[]))), - <<":a">> = iolist_to_binary(join(re:split("bbbbbac","^(b+|a){1,2}?c",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbbac","^(b+|a){1,2}?c",[]))), + <<":a">> = iolist_to_binary(join(re:split("bbbbbac","^(b+|a){1,2}?c",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("bbbbbac","^(b+|a){1,2}?c",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("bbbbbac","^(b+|a){1,2}?c",[]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("bbbbbac","^(b+|a){1,2}?c",[]))), <<"x b">> = iolist_to_binary(join(re:split("x -b","(?!\\A)x",[multiline,trim]))), +b","(?!\\A)x",[multiline,trim]))), <<"x b">> = iolist_to_binary(join(re:split("x -b","(?!\\A)x",[multiline,{parts,2}]))), +b","(?!\\A)x",[multiline,{parts,2}]))), <<"x b">> = iolist_to_binary(join(re:split("x -b","(?!\\A)x",[multiline]))), +b","(?!\\A)x",[multiline]))), <<"a">> = iolist_to_binary(join(re:split("ax","(?!\\A)x",[multiline, - trim]))), + trim]))), <<"a:">> = iolist_to_binary(join(re:split("ax","(?!\\A)x",[multiline, {parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("ax","(?!\\A)x",[multiline]))), - <<"{ab}">> = iolist_to_binary(join(re:split("{ab}","\\x0{ab}",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("ax","(?!\\A)x",[multiline]))), + <<"{ab}">> = iolist_to_binary(join(re:split("{ab}","\\x0{ab}",[trim]))), <<"{ab}">> = iolist_to_binary(join(re:split("{ab}","\\x0{ab}",[{parts, - 2}]))), - <<"{ab}">> = iolist_to_binary(join(re:split("{ab}","\\x0{ab}",[]))), - <<"">> = iolist_to_binary(join(re:split("CD","(A|B)*?CD",[trim]))), + 2}]))), + <<"{ab}">> = iolist_to_binary(join(re:split("{ab}","\\x0{ab}",[]))), + <<"">> = iolist_to_binary(join(re:split("CD","(A|B)*?CD",[trim]))), <<"::">> = iolist_to_binary(join(re:split("CD","(A|B)*?CD",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("CD","(A|B)*?CD",[]))), - <<"">> = iolist_to_binary(join(re:split("CD","(A|B)*CD",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("CD","(A|B)*?CD",[]))), + <<"">> = iolist_to_binary(join(re:split("CD","(A|B)*CD",[trim]))), <<"::">> = iolist_to_binary(join(re:split("CD","(A|B)*CD",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("CD","(A|B)*CD",[]))), - <<":AB:AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*?\\1",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("CD","(A|B)*CD",[]))), + <<":AB:AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*?\\1",[trim]))), <<":AB:AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*?\\1",[{parts, - 2}]))), - <<":AB:AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*?\\1",[]))), - <<":AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*\\1",[trim]))), + 2}]))), + <<":AB:AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*?\\1",[]))), + <<":AB">> = iolist_to_binary(join(re:split("ABABAB","(AB)*\\1",[trim]))), <<":AB:">> = iolist_to_binary(join(re:split("ABABAB","(AB)*\\1",[{parts, - 2}]))), - <<":AB:">> = iolist_to_binary(join(re:split("ABABAB","(AB)*\\1",[]))), - <<"">> = iolist_to_binary(join(re:split("foo","(?<!bar)foo",[trim]))), + 2}]))), + <<":AB:">> = iolist_to_binary(join(re:split("ABABAB","(AB)*\\1",[]))), + <<"">> = iolist_to_binary(join(re:split("foo","(?<!bar)foo",[trim]))), <<":">> = iolist_to_binary(join(re:split("foo","(?<!bar)foo",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("foo","(?<!bar)foo",[]))), - <<"cat:d">> = iolist_to_binary(join(re:split("catfood","(?<!bar)foo",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("foo","(?<!bar)foo",[]))), + <<"cat:d">> = iolist_to_binary(join(re:split("catfood","(?<!bar)foo",[trim]))), <<"cat:d">> = iolist_to_binary(join(re:split("catfood","(?<!bar)foo",[{parts, - 2}]))), - <<"cat:d">> = iolist_to_binary(join(re:split("catfood","(?<!bar)foo",[]))), - <<"ar:tle">> = iolist_to_binary(join(re:split("arfootle","(?<!bar)foo",[trim]))), + 2}]))), + <<"cat:d">> = iolist_to_binary(join(re:split("catfood","(?<!bar)foo",[]))), + <<"ar:tle">> = iolist_to_binary(join(re:split("arfootle","(?<!bar)foo",[trim]))), <<"ar:tle">> = iolist_to_binary(join(re:split("arfootle","(?<!bar)foo",[{parts, - 2}]))), - <<"ar:tle">> = iolist_to_binary(join(re:split("arfootle","(?<!bar)foo",[]))), - <<"r:sh">> = iolist_to_binary(join(re:split("rfoosh","(?<!bar)foo",[trim]))), + 2}]))), + <<"ar:tle">> = iolist_to_binary(join(re:split("arfootle","(?<!bar)foo",[]))), + <<"r:sh">> = iolist_to_binary(join(re:split("rfoosh","(?<!bar)foo",[trim]))), <<"r:sh">> = iolist_to_binary(join(re:split("rfoosh","(?<!bar)foo",[{parts, - 2}]))), - <<"r:sh">> = iolist_to_binary(join(re:split("rfoosh","(?<!bar)foo",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<!bar)foo",[trim]))), + 2}]))), + <<"r:sh">> = iolist_to_binary(join(re:split("rfoosh","(?<!bar)foo",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<!bar)foo",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<!bar)foo",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<!bar)foo",[]))), - <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<!bar)foo",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<!bar)foo",[]))), + <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<!bar)foo",[trim]))), <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<!bar)foo",[{parts, - 2}]))), - <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<!bar)foo",[]))), - <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","(?<!bar)foo",[trim]))), + 2}]))), + <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<!bar)foo",[]))), + <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","(?<!bar)foo",[trim]))), <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","(?<!bar)foo",[{parts, - 2}]))), - <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","(?<!bar)foo",[]))), - <<":d">> = iolist_to_binary(join(re:split("catfood","\\w{3}(?<!bar)foo",[trim]))), + 2}]))), + <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","(?<!bar)foo",[]))), + <<":d">> = iolist_to_binary(join(re:split("catfood","\\w{3}(?<!bar)foo",[trim]))), <<":d">> = iolist_to_binary(join(re:split("catfood","\\w{3}(?<!bar)foo",[{parts, - 2}]))), - <<":d">> = iolist_to_binary(join(re:split("catfood","\\w{3}(?<!bar)foo",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\w{3}(?<!bar)foo",[trim]))), + 2}]))), + <<":d">> = iolist_to_binary(join(re:split("catfood","\\w{3}(?<!bar)foo",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\w{3}(?<!bar)foo",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\w{3}(?<!bar)foo",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\w{3}(?<!bar)foo",[]))), - <<"foo">> = iolist_to_binary(join(re:split("foo","\\w{3}(?<!bar)foo",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\w{3}(?<!bar)foo",[]))), + <<"foo">> = iolist_to_binary(join(re:split("foo","\\w{3}(?<!bar)foo",[trim]))), <<"foo">> = iolist_to_binary(join(re:split("foo","\\w{3}(?<!bar)foo",[{parts, - 2}]))), - <<"foo">> = iolist_to_binary(join(re:split("foo","\\w{3}(?<!bar)foo",[]))), - <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","\\w{3}(?<!bar)foo",[trim]))), + 2}]))), + <<"foo">> = iolist_to_binary(join(re:split("foo","\\w{3}(?<!bar)foo",[]))), + <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","\\w{3}(?<!bar)foo",[trim]))), <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","\\w{3}(?<!bar)foo",[{parts, - 2}]))), - <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","\\w{3}(?<!bar)foo",[]))), - <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","\\w{3}(?<!bar)foo",[trim]))), + 2}]))), + <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","\\w{3}(?<!bar)foo",[]))), + <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","\\w{3}(?<!bar)foo",[trim]))), <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","\\w{3}(?<!bar)foo",[{parts, - 2}]))), - <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","\\w{3}(?<!bar)foo",[]))), - <<"fooa:foo">> = iolist_to_binary(join(re:split("fooabar","(?<=(foo)a)bar",[trim]))), + 2}]))), + <<"towbarfoo">> = iolist_to_binary(join(re:split("towbarfoo","\\w{3}(?<!bar)foo",[]))), + <<"fooa:foo">> = iolist_to_binary(join(re:split("fooabar","(?<=(foo)a)bar",[trim]))), <<"fooa:foo:">> = iolist_to_binary(join(re:split("fooabar","(?<=(foo)a)bar",[{parts, - 2}]))), - <<"fooa:foo:">> = iolist_to_binary(join(re:split("fooabar","(?<=(foo)a)bar",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo)a)bar",[trim]))), + 2}]))), + <<"fooa:foo:">> = iolist_to_binary(join(re:split("fooabar","(?<=(foo)a)bar",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo)a)bar",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo)a)bar",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo)a)bar",[]))), - <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=(foo)a)bar",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo)a)bar",[]))), + <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=(foo)a)bar",[trim]))), <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=(foo)a)bar",[{parts, - 2}]))), - <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=(foo)a)bar",[]))), - <<"foobbar">> = iolist_to_binary(join(re:split("foobbar","(?<=(foo)a)bar",[trim]))), + 2}]))), + <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=(foo)a)bar",[]))), + <<"foobbar">> = iolist_to_binary(join(re:split("foobbar","(?<=(foo)a)bar",[trim]))), <<"foobbar">> = iolist_to_binary(join(re:split("foobbar","(?<=(foo)a)bar",[{parts, - 2}]))), - <<"foobbar">> = iolist_to_binary(join(re:split("foobbar","(?<=(foo)a)bar",[]))), + 2}]))), + <<"foobbar">> = iolist_to_binary(join(re:split("foobbar","(?<=(foo)a)bar",[]))), <<"">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\z",[multiline, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\z",[multiline, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\z",[multiline]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc\\z",[multiline]))), <<"">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc\\z",[multiline]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","\\Aabc\\z",[multiline,trim]))), +abc","\\Aabc\\z",[multiline,trim]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","\\Aabc\\z",[multiline,{parts,2}]))), +abc","\\Aabc\\z",[multiline,{parts,2}]))), <<"qqq abc">> = iolist_to_binary(join(re:split("qqq -abc","\\Aabc\\z",[multiline]))), +abc","\\Aabc\\z",[multiline]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","\\Aabc\\z",[multiline,trim]))), +zzz","\\Aabc\\z",[multiline,trim]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","\\Aabc\\z",[multiline,{parts,2}]))), +zzz","\\Aabc\\z",[multiline,{parts,2}]))), <<"abc zzz">> = iolist_to_binary(join(re:split("abc -zzz","\\Aabc\\z",[multiline]))), +zzz","\\Aabc\\z",[multiline]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","\\Aabc\\z",[multiline,trim]))), +zzz","\\Aabc\\z",[multiline,trim]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","\\Aabc\\z",[multiline,{parts,2}]))), +zzz","\\Aabc\\z",[multiline,{parts,2}]))), <<"qqq abc zzz">> = iolist_to_binary(join(re:split("qqq abc -zzz","\\Aabc\\z",[multiline]))), - <<"1:.23">> = iolist_to_binary(join(re:split("1.230003938","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), +zzz","\\Aabc\\z",[multiline]))), + <<"1:.23">> = iolist_to_binary(join(re:split("1.230003938","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), <<"1:.23:">> = iolist_to_binary(join(re:split("1.230003938","(?>(\\.\\d\\d[1-9]?))\\d+",[{parts, - 2}]))), - <<"1:.23:">> = iolist_to_binary(join(re:split("1.230003938","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), - <<"1:.875">> = iolist_to_binary(join(re:split("1.875000282","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), + 2}]))), + <<"1:.23:">> = iolist_to_binary(join(re:split("1.230003938","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), + <<"1:.875">> = iolist_to_binary(join(re:split("1.875000282","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), <<"1:.875:">> = iolist_to_binary(join(re:split("1.875000282","(?>(\\.\\d\\d[1-9]?))\\d+",[{parts, - 2}]))), - <<"1:.875:">> = iolist_to_binary(join(re:split("1.875000282","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), + 2}]))), + <<"1:.875:">> = iolist_to_binary(join(re:split("1.875000282","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>(\\.\\d\\d[1-9]?))\\d+",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), - <<"1.235">> = iolist_to_binary(join(re:split("1.235","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), + <<"1.235">> = iolist_to_binary(join(re:split("1.235","(?>(\\.\\d\\d[1-9]?))\\d+",[trim]))), <<"1.235">> = iolist_to_binary(join(re:split("1.235","(?>(\\.\\d\\d[1-9]?))\\d+",[{parts, - 2}]))), - <<"1.235">> = iolist_to_binary(join(re:split("1.235","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), - <<":party">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^((?>\\w+)|(?>\\s+))*$",[trim]))), + 2}]))), + <<"1.235">> = iolist_to_binary(join(re:split("1.235","(?>(\\.\\d\\d[1-9]?))\\d+",[]))), + <<":party">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^((?>\\w+)|(?>\\s+))*$",[trim]))), <<":party:">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^((?>\\w+)|(?>\\s+))*$",[{parts, - 2}]))), - <<":party:">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^((?>\\w+)|(?>\\s+))*$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((?>\\w+)|(?>\\s+))*$",[trim]))), + 2}]))), + <<":party:">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^((?>\\w+)|(?>\\s+))*$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((?>\\w+)|(?>\\s+))*$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((?>\\w+)|(?>\\s+))*$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((?>\\w+)|(?>\\s+))*$",[]))), - <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^((?>\\w+)|(?>\\s+))*$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((?>\\w+)|(?>\\s+))*$",[]))), + <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^((?>\\w+)|(?>\\s+))*$",[trim]))), <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^((?>\\w+)|(?>\\s+))*$",[{parts, - 2}]))), - <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^((?>\\w+)|(?>\\s+))*$",[]))), - <<":12345:a">> = iolist_to_binary(join(re:split("12345a","(\\d+)(\\w)",[trim]))), + 2}]))), + <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^((?>\\w+)|(?>\\s+))*$",[]))), + <<":12345:a">> = iolist_to_binary(join(re:split("12345a","(\\d+)(\\w)",[trim]))), <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","(\\d+)(\\w)",[{parts, - 2}]))), - <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","(\\d+)(\\w)",[]))), - <<":1234:5:+">> = iolist_to_binary(join(re:split("12345+","(\\d+)(\\w)",[trim]))), + 2}]))), + <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","(\\d+)(\\w)",[]))), + <<":1234:5:+">> = iolist_to_binary(join(re:split("12345+","(\\d+)(\\w)",[trim]))), <<":1234:5:+">> = iolist_to_binary(join(re:split("12345+","(\\d+)(\\w)",[{parts, - 2}]))), - <<":1234:5:+">> = iolist_to_binary(join(re:split("12345+","(\\d+)(\\w)",[]))), - <<":12345:a">> = iolist_to_binary(join(re:split("12345a","((?>\\d+))(\\w)",[trim]))), + 2}]))), + <<":1234:5:+">> = iolist_to_binary(join(re:split("12345+","(\\d+)(\\w)",[]))), + <<":12345:a">> = iolist_to_binary(join(re:split("12345a","((?>\\d+))(\\w)",[trim]))), <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","((?>\\d+))(\\w)",[{parts, - 2}]))), - <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","((?>\\d+))(\\w)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?>\\d+))(\\w)",[trim]))), + 2}]))), + <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","((?>\\d+))(\\w)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?>\\d+))(\\w)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?>\\d+))(\\w)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?>\\d+))(\\w)",[]))), - <<"12345+">> = iolist_to_binary(join(re:split("12345+","((?>\\d+))(\\w)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?>\\d+))(\\w)",[]))), + <<"12345+">> = iolist_to_binary(join(re:split("12345+","((?>\\d+))(\\w)",[trim]))), <<"12345+">> = iolist_to_binary(join(re:split("12345+","((?>\\d+))(\\w)",[{parts, - 2}]))), - <<"12345+">> = iolist_to_binary(join(re:split("12345+","((?>\\d+))(\\w)",[]))), - <<"">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[trim]))), + 2}]))), + <<"12345+">> = iolist_to_binary(join(re:split("12345+","((?>\\d+))(\\w)",[]))), + <<"">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[]))), ok. run12() -> - <<":aaab">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[trim]))), + <<":aaab">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[trim]))), <<":aaab:">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[{parts, - 2}]))), - <<":aaab:">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[]))), - <<":aaa">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[trim]))), + 2}]))), + <<":aaab:">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[]))), + <<":aaa">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[trim]))), <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[{parts, - 2}]))), - <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[]))), - <<"aaa:ccc">> = iolist_to_binary(join(re:split("aaabbbccc","(?>b)+",[trim]))), + 2}]))), + <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[]))), + <<"aaa:ccc">> = iolist_to_binary(join(re:split("aaabbbccc","(?>b)+",[trim]))), <<"aaa:ccc">> = iolist_to_binary(join(re:split("aaabbbccc","(?>b)+",[{parts, - 2}]))), - <<"aaa:ccc">> = iolist_to_binary(join(re:split("aaabbbccc","(?>b)+",[]))), - <<"::::d">> = iolist_to_binary(join(re:split("aaabbbbccccd","(?>a+|b+|c+)*c",[trim]))), + 2}]))), + <<"aaa:ccc">> = iolist_to_binary(join(re:split("aaabbbccc","(?>b)+",[]))), + <<"::::d">> = iolist_to_binary(join(re:split("aaabbbbccccd","(?>a+|b+|c+)*c",[trim]))), <<":cccd">> = iolist_to_binary(join(re:split("aaabbbbccccd","(?>a+|b+|c+)*c",[{parts, - 2}]))), - <<"::::d">> = iolist_to_binary(join(re:split("aaabbbbccccd","(?>a+|b+|c+)*c",[]))), - <<"((:x">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[trim]))), + 2}]))), + <<"::::d">> = iolist_to_binary(join(re:split("aaabbbbccccd","(?>a+|b+|c+)*c",[]))), + <<"((:x">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[trim]))), <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[{parts, - 2}]))), - <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[]))), - <<":abc">> = iolist_to_binary(join(re:split("(abc)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[]))), + <<":abc">> = iolist_to_binary(join(re:split("(abc)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("(abc)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("(abc)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), - <<":xyz">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("(abc)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), + <<":xyz">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), <<":xyz:">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<":xyz:">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<":xyz:">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), - <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), + <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[trim]))), <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), + 2}]))), + <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(((?>[^()]+)|\\([^()]+\\))+\\)",[]))), <<"">> = iolist_to_binary(join(re:split("ab","a(?-i)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ab","a(?-i)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","a(?-i)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","a(?-i)b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("Ab","a(?-i)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("Ab","a(?-i)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("Ab","a(?-i)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("Ab","a(?-i)b",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?-i)b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?-i)b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?-i)b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?-i)b",[caseless]))), <<"aB">> = iolist_to_binary(join(re:split("aB","a(?-i)b",[caseless, - trim]))), + trim]))), <<"aB">> = iolist_to_binary(join(re:split("aB","a(?-i)b",[caseless, {parts, - 2}]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","a(?-i)b",[caseless]))), + 2}]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","a(?-i)b",[caseless]))), <<"AB">> = iolist_to_binary(join(re:split("AB","a(?-i)b",[caseless, - trim]))), + trim]))), <<"AB">> = iolist_to_binary(join(re:split("AB","a(?-i)b",[caseless, {parts, - 2}]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","a(?-i)b",[caseless]))), - <<":a bc">> = iolist_to_binary(join(re:split("a bcd e","(a (?x)b c)d e",[trim]))), + 2}]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","a(?-i)b",[caseless]))), + <<":a bc">> = iolist_to_binary(join(re:split("a bcd e","(a (?x)b c)d e",[trim]))), <<":a bc:">> = iolist_to_binary(join(re:split("a bcd e","(a (?x)b c)d e",[{parts, - 2}]))), - <<":a bc:">> = iolist_to_binary(join(re:split("a bcd e","(a (?x)b c)d e",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a (?x)b c)d e",[trim]))), + 2}]))), + <<":a bc:">> = iolist_to_binary(join(re:split("a bcd e","(a (?x)b c)d e",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a (?x)b c)d e",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a (?x)b c)d e",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a (?x)b c)d e",[]))), - <<"a b cd e">> = iolist_to_binary(join(re:split("a b cd e","(a (?x)b c)d e",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a (?x)b c)d e",[]))), + <<"a b cd e">> = iolist_to_binary(join(re:split("a b cd e","(a (?x)b c)d e",[trim]))), <<"a b cd e">> = iolist_to_binary(join(re:split("a b cd e","(a (?x)b c)d e",[{parts, - 2}]))), - <<"a b cd e">> = iolist_to_binary(join(re:split("a b cd e","(a (?x)b c)d e",[]))), - <<"abcd e">> = iolist_to_binary(join(re:split("abcd e","(a (?x)b c)d e",[trim]))), + 2}]))), + <<"a b cd e">> = iolist_to_binary(join(re:split("a b cd e","(a (?x)b c)d e",[]))), + <<"abcd e">> = iolist_to_binary(join(re:split("abcd e","(a (?x)b c)d e",[trim]))), <<"abcd e">> = iolist_to_binary(join(re:split("abcd e","(a (?x)b c)d e",[{parts, - 2}]))), - <<"abcd e">> = iolist_to_binary(join(re:split("abcd e","(a (?x)b c)d e",[]))), - <<"a bcde">> = iolist_to_binary(join(re:split("a bcde","(a (?x)b c)d e",[trim]))), + 2}]))), + <<"abcd e">> = iolist_to_binary(join(re:split("abcd e","(a (?x)b c)d e",[]))), + <<"a bcde">> = iolist_to_binary(join(re:split("a bcde","(a (?x)b c)d e",[trim]))), <<"a bcde">> = iolist_to_binary(join(re:split("a bcde","(a (?x)b c)d e",[{parts, - 2}]))), - <<"a bcde">> = iolist_to_binary(join(re:split("a bcde","(a (?x)b c)d e",[]))), - <<":a bcde f">> = iolist_to_binary(join(re:split("a bcde f","(a b(?x)c d (?-x)e f)",[trim]))), + 2}]))), + <<"a bcde">> = iolist_to_binary(join(re:split("a bcde","(a (?x)b c)d e",[]))), + <<":a bcde f">> = iolist_to_binary(join(re:split("a bcde f","(a b(?x)c d (?-x)e f)",[trim]))), <<":a bcde f:">> = iolist_to_binary(join(re:split("a bcde f","(a b(?x)c d (?-x)e f)",[{parts, - 2}]))), - <<":a bcde f:">> = iolist_to_binary(join(re:split("a bcde f","(a b(?x)c d (?-x)e f)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a b(?x)c d (?-x)e f)",[trim]))), + 2}]))), + <<":a bcde f:">> = iolist_to_binary(join(re:split("a bcde f","(a b(?x)c d (?-x)e f)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a b(?x)c d (?-x)e f)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a b(?x)c d (?-x)e f)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a b(?x)c d (?-x)e f)",[]))), - <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","(a b(?x)c d (?-x)e f)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a b(?x)c d (?-x)e f)",[]))), + <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","(a b(?x)c d (?-x)e f)",[trim]))), <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","(a b(?x)c d (?-x)e f)",[{parts, - 2}]))), - <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","(a b(?x)c d (?-x)e f)",[]))), - <<":ab">> = iolist_to_binary(join(re:split("abc","(a(?i)b)c",[trim]))), + 2}]))), + <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","(a b(?x)c d (?-x)e f)",[]))), + <<":ab">> = iolist_to_binary(join(re:split("abc","(a(?i)b)c",[trim]))), <<":ab:">> = iolist_to_binary(join(re:split("abc","(a(?i)b)c",[{parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("abc","(a(?i)b)c",[]))), - <<":aB">> = iolist_to_binary(join(re:split("aBc","(a(?i)b)c",[trim]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("abc","(a(?i)b)c",[]))), + <<":aB">> = iolist_to_binary(join(re:split("aBc","(a(?i)b)c",[trim]))), <<":aB:">> = iolist_to_binary(join(re:split("aBc","(a(?i)b)c",[{parts, - 2}]))), - <<":aB:">> = iolist_to_binary(join(re:split("aBc","(a(?i)b)c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)b)c",[trim]))), + 2}]))), + <<":aB:">> = iolist_to_binary(join(re:split("aBc","(a(?i)b)c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)b)c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)b)c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)b)c",[]))), - <<"abC">> = iolist_to_binary(join(re:split("abC","(a(?i)b)c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)b)c",[]))), + <<"abC">> = iolist_to_binary(join(re:split("abC","(a(?i)b)c",[trim]))), <<"abC">> = iolist_to_binary(join(re:split("abC","(a(?i)b)c",[{parts, - 2}]))), - <<"abC">> = iolist_to_binary(join(re:split("abC","(a(?i)b)c",[]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","(a(?i)b)c",[trim]))), + 2}]))), + <<"abC">> = iolist_to_binary(join(re:split("abC","(a(?i)b)c",[]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","(a(?i)b)c",[trim]))), <<"aBC">> = iolist_to_binary(join(re:split("aBC","(a(?i)b)c",[{parts, - 2}]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","(a(?i)b)c",[]))), - <<"Abc">> = iolist_to_binary(join(re:split("Abc","(a(?i)b)c",[trim]))), + 2}]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","(a(?i)b)c",[]))), + <<"Abc">> = iolist_to_binary(join(re:split("Abc","(a(?i)b)c",[trim]))), <<"Abc">> = iolist_to_binary(join(re:split("Abc","(a(?i)b)c",[{parts, - 2}]))), - <<"Abc">> = iolist_to_binary(join(re:split("Abc","(a(?i)b)c",[]))), - <<"ABc">> = iolist_to_binary(join(re:split("ABc","(a(?i)b)c",[trim]))), + 2}]))), + <<"Abc">> = iolist_to_binary(join(re:split("Abc","(a(?i)b)c",[]))), + <<"ABc">> = iolist_to_binary(join(re:split("ABc","(a(?i)b)c",[trim]))), <<"ABc">> = iolist_to_binary(join(re:split("ABc","(a(?i)b)c",[{parts, - 2}]))), - <<"ABc">> = iolist_to_binary(join(re:split("ABc","(a(?i)b)c",[]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","(a(?i)b)c",[trim]))), + 2}]))), + <<"ABc">> = iolist_to_binary(join(re:split("ABc","(a(?i)b)c",[]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","(a(?i)b)c",[trim]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","(a(?i)b)c",[{parts, - 2}]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","(a(?i)b)c",[]))), - <<"AbC">> = iolist_to_binary(join(re:split("AbC","(a(?i)b)c",[trim]))), + 2}]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","(a(?i)b)c",[]))), + <<"AbC">> = iolist_to_binary(join(re:split("AbC","(a(?i)b)c",[trim]))), <<"AbC">> = iolist_to_binary(join(re:split("AbC","(a(?i)b)c",[{parts, - 2}]))), - <<"AbC">> = iolist_to_binary(join(re:split("AbC","(a(?i)b)c",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","a(?i:b)c",[trim]))), + 2}]))), + <<"AbC">> = iolist_to_binary(join(re:split("AbC","(a(?i)b)c",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","a(?i:b)c",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","a(?i:b)c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","a(?i:b)c",[]))), - <<"">> = iolist_to_binary(join(re:split("aBc","a(?i:b)c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","a(?i:b)c",[]))), + <<"">> = iolist_to_binary(join(re:split("aBc","a(?i:b)c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aBc","a(?i:b)c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aBc","a(?i:b)c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aBc","a(?i:b)c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)c",[]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","a(?i:b)c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)c",[]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","a(?i:b)c",[trim]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","a(?i:b)c",[{parts, - 2}]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","a(?i:b)c",[]))), - <<"abC">> = iolist_to_binary(join(re:split("abC","a(?i:b)c",[trim]))), + 2}]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","a(?i:b)c",[]))), + <<"abC">> = iolist_to_binary(join(re:split("abC","a(?i:b)c",[trim]))), <<"abC">> = iolist_to_binary(join(re:split("abC","a(?i:b)c",[{parts, - 2}]))), - <<"abC">> = iolist_to_binary(join(re:split("abC","a(?i:b)c",[]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)c",[trim]))), + 2}]))), + <<"abC">> = iolist_to_binary(join(re:split("abC","a(?i:b)c",[]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)c",[trim]))), <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)c",[{parts, - 2}]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)c",[]))), - <<"">> = iolist_to_binary(join(re:split("aBc","a(?i:b)*c",[trim]))), + 2}]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)c",[]))), + <<"">> = iolist_to_binary(join(re:split("aBc","a(?i:b)*c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aBc","a(?i:b)*c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aBc","a(?i:b)*c",[]))), - <<"">> = iolist_to_binary(join(re:split("aBBc","a(?i:b)*c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aBc","a(?i:b)*c",[]))), + <<"">> = iolist_to_binary(join(re:split("aBBc","a(?i:b)*c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aBBc","a(?i:b)*c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aBBc","a(?i:b)*c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)*c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aBBc","a(?i:b)*c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)*c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)*c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)*c",[]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)*c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?i:b)*c",[]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)*c",[trim]))), <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)*c",[{parts, - 2}]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)*c",[]))), - <<"aBBC">> = iolist_to_binary(join(re:split("aBBC","a(?i:b)*c",[trim]))), + 2}]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","a(?i:b)*c",[]))), + <<"aBBC">> = iolist_to_binary(join(re:split("aBBC","a(?i:b)*c",[trim]))), <<"aBBC">> = iolist_to_binary(join(re:split("aBBC","a(?i:b)*c",[{parts, - 2}]))), - <<"aBBC">> = iolist_to_binary(join(re:split("aBBC","a(?i:b)*c",[]))), - <<"">> = iolist_to_binary(join(re:split("abcd","a(?=b(?i)c)\\w\\wd",[trim]))), + 2}]))), + <<"aBBC">> = iolist_to_binary(join(re:split("aBBC","a(?i:b)*c",[]))), + <<"">> = iolist_to_binary(join(re:split("abcd","a(?=b(?i)c)\\w\\wd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcd","a(?=b(?i)c)\\w\\wd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcd","a(?=b(?i)c)\\w\\wd",[]))), - <<"">> = iolist_to_binary(join(re:split("abCd","a(?=b(?i)c)\\w\\wd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcd","a(?=b(?i)c)\\w\\wd",[]))), + <<"">> = iolist_to_binary(join(re:split("abCd","a(?=b(?i)c)\\w\\wd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abCd","a(?=b(?i)c)\\w\\wd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abCd","a(?=b(?i)c)\\w\\wd",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?=b(?i)c)\\w\\wd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abCd","a(?=b(?i)c)\\w\\wd",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?=b(?i)c)\\w\\wd",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?=b(?i)c)\\w\\wd",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?=b(?i)c)\\w\\wd",[]))), - <<"aBCd">> = iolist_to_binary(join(re:split("aBCd","a(?=b(?i)c)\\w\\wd",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?=b(?i)c)\\w\\wd",[]))), + <<"aBCd">> = iolist_to_binary(join(re:split("aBCd","a(?=b(?i)c)\\w\\wd",[trim]))), <<"aBCd">> = iolist_to_binary(join(re:split("aBCd","a(?=b(?i)c)\\w\\wd",[{parts, - 2}]))), - <<"aBCd">> = iolist_to_binary(join(re:split("aBCd","a(?=b(?i)c)\\w\\wd",[]))), - <<"abcD">> = iolist_to_binary(join(re:split("abcD","a(?=b(?i)c)\\w\\wd",[trim]))), + 2}]))), + <<"aBCd">> = iolist_to_binary(join(re:split("aBCd","a(?=b(?i)c)\\w\\wd",[]))), + <<"abcD">> = iolist_to_binary(join(re:split("abcD","a(?=b(?i)c)\\w\\wd",[trim]))), <<"abcD">> = iolist_to_binary(join(re:split("abcD","a(?=b(?i)c)\\w\\wd",[{parts, - 2}]))), - <<"abcD">> = iolist_to_binary(join(re:split("abcD","a(?=b(?i)c)\\w\\wd",[]))), + 2}]))), + <<"abcD">> = iolist_to_binary(join(re:split("abcD","a(?=b(?i)c)\\w\\wd",[]))), <<"">> = iolist_to_binary(join(re:split("more than million","(?s-i:more.*than).*million",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("more than million","(?s-i:more.*than).*million",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("more than million","(?s-i:more.*than).*million",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("more than million","(?s-i:more.*than).*million",[caseless]))), <<"">> = iolist_to_binary(join(re:split("more than MILLION","(?s-i:more.*than).*million",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("more than MILLION","(?s-i:more.*than).*million",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("more than MILLION","(?s-i:more.*than).*million",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("more than MILLION","(?s-i:more.*than).*million",[caseless]))), <<"">> = iolist_to_binary(join(re:split("more - than Million","(?s-i:more.*than).*million",[caseless,trim]))), + than Million","(?s-i:more.*than).*million",[caseless,trim]))), <<":">> = iolist_to_binary(join(re:split("more - than Million","(?s-i:more.*than).*million",[caseless,{parts,2}]))), + than Million","(?s-i:more.*than).*million",[caseless,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("more - than Million","(?s-i:more.*than).*million",[caseless]))), + than Million","(?s-i:more.*than).*million",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s-i:more.*than).*million",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s-i:more.*than).*million",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s-i:more.*than).*million",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?s-i:more.*than).*million",[caseless]))), <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?s-i:more.*than).*million",[caseless, - trim]))), + trim]))), <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?s-i:more.*than).*million",[caseless, {parts, - 2}]))), - <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?s-i:more.*than).*million",[caseless]))), + 2}]))), + <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?s-i:more.*than).*million",[caseless]))), <<"more than million">> = iolist_to_binary(join(re:split("more than - million","(?s-i:more.*than).*million",[caseless,trim]))), + million","(?s-i:more.*than).*million",[caseless,trim]))), <<"more than million">> = iolist_to_binary(join(re:split("more than - million","(?s-i:more.*than).*million",[caseless,{parts,2}]))), + million","(?s-i:more.*than).*million",[caseless,{parts,2}]))), <<"more than million">> = iolist_to_binary(join(re:split("more than - million","(?s-i:more.*than).*million",[caseless]))), + million","(?s-i:more.*than).*million",[caseless]))), <<"">> = iolist_to_binary(join(re:split("more than million","(?:(?s-i)more.*than).*million",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("more than million","(?:(?s-i)more.*than).*million",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("more than million","(?:(?s-i)more.*than).*million",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("more than million","(?:(?s-i)more.*than).*million",[caseless]))), <<"">> = iolist_to_binary(join(re:split("more than MILLION","(?:(?s-i)more.*than).*million",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("more than MILLION","(?:(?s-i)more.*than).*million",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("more than MILLION","(?:(?s-i)more.*than).*million",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("more than MILLION","(?:(?s-i)more.*than).*million",[caseless]))), <<"">> = iolist_to_binary(join(re:split("more - than Million","(?:(?s-i)more.*than).*million",[caseless,trim]))), + than Million","(?:(?s-i)more.*than).*million",[caseless,trim]))), <<":">> = iolist_to_binary(join(re:split("more - than Million","(?:(?s-i)more.*than).*million",[caseless,{parts,2}]))), + than Million","(?:(?s-i)more.*than).*million",[caseless,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("more - than Million","(?:(?s-i)more.*than).*million",[caseless]))), + than Million","(?:(?s-i)more.*than).*million",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?s-i)more.*than).*million",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?s-i)more.*than).*million",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?s-i)more.*than).*million",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?s-i)more.*than).*million",[caseless]))), <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?:(?s-i)more.*than).*million",[caseless, - trim]))), + trim]))), <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?:(?s-i)more.*than).*million",[caseless, {parts, - 2}]))), - <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?:(?s-i)more.*than).*million",[caseless]))), + 2}]))), + <<"MORE THAN MILLION">> = iolist_to_binary(join(re:split("MORE THAN MILLION","(?:(?s-i)more.*than).*million",[caseless]))), <<"more than million">> = iolist_to_binary(join(re:split("more than - million","(?:(?s-i)more.*than).*million",[caseless,trim]))), + million","(?:(?s-i)more.*than).*million",[caseless,trim]))), <<"more than million">> = iolist_to_binary(join(re:split("more than - million","(?:(?s-i)more.*than).*million",[caseless,{parts,2}]))), + million","(?:(?s-i)more.*than).*million",[caseless,{parts,2}]))), <<"more than million">> = iolist_to_binary(join(re:split("more than - million","(?:(?s-i)more.*than).*million",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("abc","(?>a(?i)b+)+c",[trim]))), + million","(?:(?s-i)more.*than).*million",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("abc","(?>a(?i)b+)+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","(?>a(?i)b+)+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aBbc","(?>a(?i)b+)+c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","(?>a(?i)b+)+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aBbc","(?>a(?i)b+)+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aBbc","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aBbc","(?>a(?i)b+)+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aBBc","(?>a(?i)b+)+c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aBbc","(?>a(?i)b+)+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aBBc","(?>a(?i)b+)+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aBBc","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aBBc","(?>a(?i)b+)+c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>a(?i)b+)+c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aBBc","(?>a(?i)b+)+c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>a(?i)b+)+c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>a(?i)b+)+c",[]))), - <<"Abc">> = iolist_to_binary(join(re:split("Abc","(?>a(?i)b+)+c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>a(?i)b+)+c",[]))), + <<"Abc">> = iolist_to_binary(join(re:split("Abc","(?>a(?i)b+)+c",[trim]))), <<"Abc">> = iolist_to_binary(join(re:split("Abc","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<"Abc">> = iolist_to_binary(join(re:split("Abc","(?>a(?i)b+)+c",[]))), - <<"abAb">> = iolist_to_binary(join(re:split("abAb","(?>a(?i)b+)+c",[trim]))), + 2}]))), + <<"Abc">> = iolist_to_binary(join(re:split("Abc","(?>a(?i)b+)+c",[]))), + <<"abAb">> = iolist_to_binary(join(re:split("abAb","(?>a(?i)b+)+c",[trim]))), <<"abAb">> = iolist_to_binary(join(re:split("abAb","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<"abAb">> = iolist_to_binary(join(re:split("abAb","(?>a(?i)b+)+c",[]))), - <<"abbC">> = iolist_to_binary(join(re:split("abbC","(?>a(?i)b+)+c",[trim]))), + 2}]))), + <<"abAb">> = iolist_to_binary(join(re:split("abAb","(?>a(?i)b+)+c",[]))), + <<"abbC">> = iolist_to_binary(join(re:split("abbC","(?>a(?i)b+)+c",[trim]))), <<"abbC">> = iolist_to_binary(join(re:split("abbC","(?>a(?i)b+)+c",[{parts, - 2}]))), - <<"abbC">> = iolist_to_binary(join(re:split("abbC","(?>a(?i)b+)+c",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","(?=a(?i)b)\\w\\wc",[trim]))), + 2}]))), + <<"abbC">> = iolist_to_binary(join(re:split("abbC","(?>a(?i)b+)+c",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","(?=a(?i)b)\\w\\wc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","(?=a(?i)b)\\w\\wc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","(?=a(?i)b)\\w\\wc",[]))), - <<"">> = iolist_to_binary(join(re:split("aBc","(?=a(?i)b)\\w\\wc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","(?=a(?i)b)\\w\\wc",[]))), + <<"">> = iolist_to_binary(join(re:split("aBc","(?=a(?i)b)\\w\\wc",[trim]))), <<":">> = iolist_to_binary(join(re:split("aBc","(?=a(?i)b)\\w\\wc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aBc","(?=a(?i)b)\\w\\wc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=a(?i)b)\\w\\wc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aBc","(?=a(?i)b)\\w\\wc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=a(?i)b)\\w\\wc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=a(?i)b)\\w\\wc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=a(?i)b)\\w\\wc",[]))), - <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?=a(?i)b)\\w\\wc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?=a(?i)b)\\w\\wc",[]))), + <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?=a(?i)b)\\w\\wc",[trim]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?=a(?i)b)\\w\\wc",[{parts, - 2}]))), - <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?=a(?i)b)\\w\\wc",[]))), - <<"abC">> = iolist_to_binary(join(re:split("abC","(?=a(?i)b)\\w\\wc",[trim]))), + 2}]))), + <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?=a(?i)b)\\w\\wc",[]))), + <<"abC">> = iolist_to_binary(join(re:split("abC","(?=a(?i)b)\\w\\wc",[trim]))), <<"abC">> = iolist_to_binary(join(re:split("abC","(?=a(?i)b)\\w\\wc",[{parts, - 2}]))), - <<"abC">> = iolist_to_binary(join(re:split("abC","(?=a(?i)b)\\w\\wc",[]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","(?=a(?i)b)\\w\\wc",[trim]))), + 2}]))), + <<"abC">> = iolist_to_binary(join(re:split("abC","(?=a(?i)b)\\w\\wc",[]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","(?=a(?i)b)\\w\\wc",[trim]))), <<"aBC">> = iolist_to_binary(join(re:split("aBC","(?=a(?i)b)\\w\\wc",[{parts, - 2}]))), - <<"aBC">> = iolist_to_binary(join(re:split("aBC","(?=a(?i)b)\\w\\wc",[]))), - <<"ab:xx">> = iolist_to_binary(join(re:split("abxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), + 2}]))), + <<"aBC">> = iolist_to_binary(join(re:split("aBC","(?=a(?i)b)\\w\\wc",[]))), + <<"ab:xx">> = iolist_to_binary(join(re:split("abxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), <<"ab:xx:">> = iolist_to_binary(join(re:split("abxxc","(?<=a(?i)b)(\\w\\w)c",[{parts, - 2}]))), - <<"ab:xx:">> = iolist_to_binary(join(re:split("abxxc","(?<=a(?i)b)(\\w\\w)c",[]))), - <<"aB:xx">> = iolist_to_binary(join(re:split("aBxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), + 2}]))), + <<"ab:xx:">> = iolist_to_binary(join(re:split("abxxc","(?<=a(?i)b)(\\w\\w)c",[]))), + <<"aB:xx">> = iolist_to_binary(join(re:split("aBxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), <<"aB:xx:">> = iolist_to_binary(join(re:split("aBxxc","(?<=a(?i)b)(\\w\\w)c",[{parts, - 2}]))), - <<"aB:xx:">> = iolist_to_binary(join(re:split("aBxxc","(?<=a(?i)b)(\\w\\w)c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a(?i)b)(\\w\\w)c",[trim]))), + 2}]))), + <<"aB:xx:">> = iolist_to_binary(join(re:split("aBxxc","(?<=a(?i)b)(\\w\\w)c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a(?i)b)(\\w\\w)c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a(?i)b)(\\w\\w)c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a(?i)b)(\\w\\w)c",[]))), - <<"Abxxc">> = iolist_to_binary(join(re:split("Abxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a(?i)b)(\\w\\w)c",[]))), + <<"Abxxc">> = iolist_to_binary(join(re:split("Abxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), <<"Abxxc">> = iolist_to_binary(join(re:split("Abxxc","(?<=a(?i)b)(\\w\\w)c",[{parts, - 2}]))), - <<"Abxxc">> = iolist_to_binary(join(re:split("Abxxc","(?<=a(?i)b)(\\w\\w)c",[]))), - <<"ABxxc">> = iolist_to_binary(join(re:split("ABxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), + 2}]))), + <<"Abxxc">> = iolist_to_binary(join(re:split("Abxxc","(?<=a(?i)b)(\\w\\w)c",[]))), + <<"ABxxc">> = iolist_to_binary(join(re:split("ABxxc","(?<=a(?i)b)(\\w\\w)c",[trim]))), <<"ABxxc">> = iolist_to_binary(join(re:split("ABxxc","(?<=a(?i)b)(\\w\\w)c",[{parts, - 2}]))), - <<"ABxxc">> = iolist_to_binary(join(re:split("ABxxc","(?<=a(?i)b)(\\w\\w)c",[]))), - <<"abxxC">> = iolist_to_binary(join(re:split("abxxC","(?<=a(?i)b)(\\w\\w)c",[trim]))), + 2}]))), + <<"ABxxc">> = iolist_to_binary(join(re:split("ABxxc","(?<=a(?i)b)(\\w\\w)c",[]))), + <<"abxxC">> = iolist_to_binary(join(re:split("abxxC","(?<=a(?i)b)(\\w\\w)c",[trim]))), <<"abxxC">> = iolist_to_binary(join(re:split("abxxC","(?<=a(?i)b)(\\w\\w)c",[{parts, - 2}]))), - <<"abxxC">> = iolist_to_binary(join(re:split("abxxC","(?<=a(?i)b)(\\w\\w)c",[]))), - <<":a">> = iolist_to_binary(join(re:split("aA","(?:(a)|b)(?(1)A|B)",[trim]))), + 2}]))), + <<"abxxC">> = iolist_to_binary(join(re:split("abxxC","(?<=a(?i)b)(\\w\\w)c",[]))), + <<":a">> = iolist_to_binary(join(re:split("aA","(?:(a)|b)(?(1)A|B)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aA","(?:(a)|b)(?(1)A|B)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aA","(?:(a)|b)(?(1)A|B)",[]))), - <<"">> = iolist_to_binary(join(re:split("bB","(?:(a)|b)(?(1)A|B)",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aA","(?:(a)|b)(?(1)A|B)",[]))), + <<"">> = iolist_to_binary(join(re:split("bB","(?:(a)|b)(?(1)A|B)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("bB","(?:(a)|b)(?(1)A|B)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("bB","(?:(a)|b)(?(1)A|B)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(a)|b)(?(1)A|B)",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("bB","(?:(a)|b)(?(1)A|B)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(a)|b)(?(1)A|B)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(a)|b)(?(1)A|B)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(a)|b)(?(1)A|B)",[]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(a)|b)(?(1)A|B)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(a)|b)(?(1)A|B)",[]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(a)|b)(?(1)A|B)",[trim]))), <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(a)|b)(?(1)A|B)",[{parts, - 2}]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(a)|b)(?(1)A|B)",[]))), - <<"bA">> = iolist_to_binary(join(re:split("bA","(?:(a)|b)(?(1)A|B)",[trim]))), + 2}]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(a)|b)(?(1)A|B)",[]))), + <<"bA">> = iolist_to_binary(join(re:split("bA","(?:(a)|b)(?(1)A|B)",[trim]))), <<"bA">> = iolist_to_binary(join(re:split("bA","(?:(a)|b)(?(1)A|B)",[{parts, - 2}]))), - <<"bA">> = iolist_to_binary(join(re:split("bA","(?:(a)|b)(?(1)A|B)",[]))), - <<":a">> = iolist_to_binary(join(re:split("aa","^(a)?(?(1)a|b)+$",[trim]))), + 2}]))), + <<"bA">> = iolist_to_binary(join(re:split("bA","(?:(a)|b)(?(1)A|B)",[]))), + <<":a">> = iolist_to_binary(join(re:split("aa","^(a)?(?(1)a|b)+$",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aa","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aa","^(a)?(?(1)a|b)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^(a)?(?(1)a|b)+$",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aa","^(a)?(?(1)a|b)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^(a)?(?(1)a|b)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","^(a)?(?(1)a|b)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("bb","^(a)?(?(1)a|b)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","^(a)?(?(1)a|b)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("bb","^(a)?(?(1)a|b)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("bb","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("bb","^(a)?(?(1)a|b)+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("bb","^(a)?(?(1)a|b)+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a)?(?(1)a|b)+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a)?(?(1)a|b)+$",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(a)?(?(1)a|b)+$",[]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(a)?(?(1)a|b)+$",[]))), ok. run13() -> - <<"">> = iolist_to_binary(join(re:split("abc:","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), + <<"">> = iolist_to_binary(join(re:split("abc:","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc:","^(?(?=abc)\\w{3}:|\\d\\d)$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc:","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), - <<"">> = iolist_to_binary(join(re:split("12","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc:","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), + <<"">> = iolist_to_binary(join(re:split("12","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), <<":">> = iolist_to_binary(join(re:split("12","^(?(?=abc)\\w{3}:|\\d\\d)$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?=abc)\\w{3}:|\\d\\d)$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), - <<"123">> = iolist_to_binary(join(re:split("123","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), + <<"123">> = iolist_to_binary(join(re:split("123","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), <<"123">> = iolist_to_binary(join(re:split("123","^(?(?=abc)\\w{3}:|\\d\\d)$",[{parts, - 2}]))), - <<"123">> = iolist_to_binary(join(re:split("123","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), + 2}]))), + <<"123">> = iolist_to_binary(join(re:split("123","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?=abc)\\w{3}:|\\d\\d)$",[trim]))), <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?=abc)\\w{3}:|\\d\\d)$",[{parts, - 2}]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), - <<"">> = iolist_to_binary(join(re:split("abc:","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), + 2}]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?=abc)\\w{3}:|\\d\\d)$",[]))), + <<"">> = iolist_to_binary(join(re:split("abc:","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc:","^(?(?!abc)\\d\\d|\\w{3}:)$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc:","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), - <<"">> = iolist_to_binary(join(re:split("12","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc:","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), + <<"">> = iolist_to_binary(join(re:split("12","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), <<":">> = iolist_to_binary(join(re:split("12","^(?(?!abc)\\d\\d|\\w{3}:)$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?!abc)\\d\\d|\\w{3}:)$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), - <<"123">> = iolist_to_binary(join(re:split("123","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), + <<"123">> = iolist_to_binary(join(re:split("123","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), <<"123">> = iolist_to_binary(join(re:split("123","^(?(?!abc)\\d\\d|\\w{3}:)$",[{parts, - 2}]))), - <<"123">> = iolist_to_binary(join(re:split("123","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), + 2}]))), + <<"123">> = iolist_to_binary(join(re:split("123","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?!abc)\\d\\d|\\w{3}:)$",[trim]))), <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?!abc)\\d\\d|\\w{3}:)$",[{parts, - 2}]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), - <<"foo">> = iolist_to_binary(join(re:split("foobar","(?(?<=foo)bar|cat)",[trim]))), + 2}]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","^(?(?!abc)\\d\\d|\\w{3}:)$",[]))), + <<"foo">> = iolist_to_binary(join(re:split("foobar","(?(?<=foo)bar|cat)",[trim]))), <<"foo:">> = iolist_to_binary(join(re:split("foobar","(?(?<=foo)bar|cat)",[{parts, - 2}]))), - <<"foo:">> = iolist_to_binary(join(re:split("foobar","(?(?<=foo)bar|cat)",[]))), - <<"">> = iolist_to_binary(join(re:split("cat","(?(?<=foo)bar|cat)",[trim]))), + 2}]))), + <<"foo:">> = iolist_to_binary(join(re:split("foobar","(?(?<=foo)bar|cat)",[]))), + <<"">> = iolist_to_binary(join(re:split("cat","(?(?<=foo)bar|cat)",[trim]))), <<":">> = iolist_to_binary(join(re:split("cat","(?(?<=foo)bar|cat)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("cat","(?(?<=foo)bar|cat)",[]))), - <<"f">> = iolist_to_binary(join(re:split("fcat","(?(?<=foo)bar|cat)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("cat","(?(?<=foo)bar|cat)",[]))), + <<"f">> = iolist_to_binary(join(re:split("fcat","(?(?<=foo)bar|cat)",[trim]))), <<"f:">> = iolist_to_binary(join(re:split("fcat","(?(?<=foo)bar|cat)",[{parts, - 2}]))), - <<"f:">> = iolist_to_binary(join(re:split("fcat","(?(?<=foo)bar|cat)",[]))), - <<"fo">> = iolist_to_binary(join(re:split("focat","(?(?<=foo)bar|cat)",[trim]))), + 2}]))), + <<"f:">> = iolist_to_binary(join(re:split("fcat","(?(?<=foo)bar|cat)",[]))), + <<"fo">> = iolist_to_binary(join(re:split("focat","(?(?<=foo)bar|cat)",[trim]))), <<"fo:">> = iolist_to_binary(join(re:split("focat","(?(?<=foo)bar|cat)",[{parts, - 2}]))), - <<"fo:">> = iolist_to_binary(join(re:split("focat","(?(?<=foo)bar|cat)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<=foo)bar|cat)",[trim]))), + 2}]))), + <<"fo:">> = iolist_to_binary(join(re:split("focat","(?(?<=foo)bar|cat)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<=foo)bar|cat)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<=foo)bar|cat)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<=foo)bar|cat)",[]))), - <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<=foo)bar|cat)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<=foo)bar|cat)",[]))), + <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<=foo)bar|cat)",[trim]))), <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<=foo)bar|cat)",[{parts, - 2}]))), - <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<=foo)bar|cat)",[]))), - <<"foo">> = iolist_to_binary(join(re:split("foobar","(?(?<!foo)cat|bar)",[trim]))), + 2}]))), + <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<=foo)bar|cat)",[]))), + <<"foo">> = iolist_to_binary(join(re:split("foobar","(?(?<!foo)cat|bar)",[trim]))), <<"foo:">> = iolist_to_binary(join(re:split("foobar","(?(?<!foo)cat|bar)",[{parts, - 2}]))), - <<"foo:">> = iolist_to_binary(join(re:split("foobar","(?(?<!foo)cat|bar)",[]))), - <<"">> = iolist_to_binary(join(re:split("cat","(?(?<!foo)cat|bar)",[trim]))), + 2}]))), + <<"foo:">> = iolist_to_binary(join(re:split("foobar","(?(?<!foo)cat|bar)",[]))), + <<"">> = iolist_to_binary(join(re:split("cat","(?(?<!foo)cat|bar)",[trim]))), <<":">> = iolist_to_binary(join(re:split("cat","(?(?<!foo)cat|bar)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("cat","(?(?<!foo)cat|bar)",[]))), - <<"f">> = iolist_to_binary(join(re:split("fcat","(?(?<!foo)cat|bar)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("cat","(?(?<!foo)cat|bar)",[]))), + <<"f">> = iolist_to_binary(join(re:split("fcat","(?(?<!foo)cat|bar)",[trim]))), <<"f:">> = iolist_to_binary(join(re:split("fcat","(?(?<!foo)cat|bar)",[{parts, - 2}]))), - <<"f:">> = iolist_to_binary(join(re:split("fcat","(?(?<!foo)cat|bar)",[]))), - <<"fo">> = iolist_to_binary(join(re:split("focat","(?(?<!foo)cat|bar)",[trim]))), + 2}]))), + <<"f:">> = iolist_to_binary(join(re:split("fcat","(?(?<!foo)cat|bar)",[]))), + <<"fo">> = iolist_to_binary(join(re:split("focat","(?(?<!foo)cat|bar)",[trim]))), <<"fo:">> = iolist_to_binary(join(re:split("focat","(?(?<!foo)cat|bar)",[{parts, - 2}]))), - <<"fo:">> = iolist_to_binary(join(re:split("focat","(?(?<!foo)cat|bar)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<!foo)cat|bar)",[trim]))), + 2}]))), + <<"fo:">> = iolist_to_binary(join(re:split("focat","(?(?<!foo)cat|bar)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<!foo)cat|bar)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<!foo)cat|bar)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<!foo)cat|bar)",[]))), - <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<!foo)cat|bar)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?<!foo)cat|bar)",[]))), + <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<!foo)cat|bar)",[trim]))), <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<!foo)cat|bar)",[{parts, - 2}]))), - <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<!foo)cat|bar)",[]))), + 2}]))), + <<"foocat">> = iolist_to_binary(join(re:split("foocat","(?(?<!foo)cat|bar)",[]))), <<"">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended, - trim]))), + trim]))), <<"::">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), <<":(">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) |) ",[extended, - trim]))), + trim]))), <<":(:">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) |) ",[extended, {parts, - 2}]))), - <<":(:">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), + 2}]))), + <<":(:">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), <<":::(">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) |) ",[extended, - trim]))), + trim]))), <<"::(abcd) fox">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) |) ",[extended, {parts, - 2}]))), - <<":::(:::">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), + 2}]))), + <<":::(:::">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), <<"(">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended, - trim]))), + trim]))), <<"(::">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended, {parts, - 2}]))), - <<"(::">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), + 2}]))), + <<"(::">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) |) ",[extended]))), <<"">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended, - trim]))), + trim]))), <<"::">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), <<":(">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) ) ",[extended, - trim]))), + trim]))), <<":(:">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) ) ",[extended, {parts, - 2}]))), - <<":(:">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), + 2}]))), + <<":(:">> = iolist_to_binary(join(re:split("(abcd)","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), <<":::(">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) ) ",[extended, - trim]))), + trim]))), <<"::(abcd) fox">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) ) ",[extended, {parts, - 2}]))), - <<":::(:::">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), + 2}]))), + <<":::(:::">> = iolist_to_binary(join(re:split("the quick (abcd) fox","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), <<"(">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended, - trim]))), + trim]))), <<"(::">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended, {parts, - 2}]))), - <<"(::">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), - <<":1:2">> = iolist_to_binary(join(re:split("12","^(?(2)a|(1)(2))+$",[trim]))), + 2}]))), + <<"(::">> = iolist_to_binary(join(re:split("(abcd","( \\( )? [^()]+ (?(1) \\) ) ",[extended]))), + <<":1:2">> = iolist_to_binary(join(re:split("12","^(?(2)a|(1)(2))+$",[trim]))), <<":1:2:">> = iolist_to_binary(join(re:split("12","^(?(2)a|(1)(2))+$",[{parts, - 2}]))), - <<":1:2:">> = iolist_to_binary(join(re:split("12","^(?(2)a|(1)(2))+$",[]))), - <<":1:2">> = iolist_to_binary(join(re:split("12a","^(?(2)a|(1)(2))+$",[trim]))), + 2}]))), + <<":1:2:">> = iolist_to_binary(join(re:split("12","^(?(2)a|(1)(2))+$",[]))), + <<":1:2">> = iolist_to_binary(join(re:split("12a","^(?(2)a|(1)(2))+$",[trim]))), <<":1:2:">> = iolist_to_binary(join(re:split("12a","^(?(2)a|(1)(2))+$",[{parts, - 2}]))), - <<":1:2:">> = iolist_to_binary(join(re:split("12a","^(?(2)a|(1)(2))+$",[]))), - <<":1:2">> = iolist_to_binary(join(re:split("12aa","^(?(2)a|(1)(2))+$",[trim]))), + 2}]))), + <<":1:2:">> = iolist_to_binary(join(re:split("12a","^(?(2)a|(1)(2))+$",[]))), + <<":1:2">> = iolist_to_binary(join(re:split("12aa","^(?(2)a|(1)(2))+$",[trim]))), <<":1:2:">> = iolist_to_binary(join(re:split("12aa","^(?(2)a|(1)(2))+$",[{parts, - 2}]))), - <<":1:2:">> = iolist_to_binary(join(re:split("12aa","^(?(2)a|(1)(2))+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(2)a|(1)(2))+$",[trim]))), + 2}]))), + <<":1:2:">> = iolist_to_binary(join(re:split("12aa","^(?(2)a|(1)(2))+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(2)a|(1)(2))+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(2)a|(1)(2))+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(2)a|(1)(2))+$",[]))), - <<"1234">> = iolist_to_binary(join(re:split("1234","^(?(2)a|(1)(2))+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?(2)a|(1)(2))+$",[]))), + <<"1234">> = iolist_to_binary(join(re:split("1234","^(?(2)a|(1)(2))+$",[trim]))), <<"1234">> = iolist_to_binary(join(re:split("1234","^(?(2)a|(1)(2))+$",[{parts, - 2}]))), - <<"1234">> = iolist_to_binary(join(re:split("1234","^(?(2)a|(1)(2))+$",[]))), - <<":blah">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<"1234">> = iolist_to_binary(join(re:split("1234","^(?(2)a|(1)(2))+$",[]))), + <<":blah">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+\\1",[trim]))), <<":blah:">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<":blah:">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+\\1",[]))), - <<":BLAH">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<":blah:">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+\\1",[]))), + <<":BLAH">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+\\1",[trim]))), <<":BLAH:">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<":BLAH:">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+\\1",[]))), - <<":Blah">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<":BLAH:">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+\\1",[]))), + <<":Blah">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+\\1",[trim]))), <<":Blah:">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<":Blah:">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+\\1",[]))), - <<":blaH">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<":Blah:">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+\\1",[]))), + <<":blaH">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+\\1",[trim]))), <<":blaH:">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<":blaH:">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+\\1",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<":blaH:">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+\\1",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)blah)\\s+\\1",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)blah)\\s+\\1",[]))), - <<"blah BLAH">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)blah)\\s+\\1",[]))), + <<"blah BLAH">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+\\1",[trim]))), <<"blah BLAH">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<"blah BLAH">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+\\1",[]))), - <<"Blah blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<"blah BLAH">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+\\1",[]))), + <<"Blah blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+\\1",[trim]))), <<"Blah blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<"Blah blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+\\1",[]))), - <<"blaH blah">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+\\1",[trim]))), + 2}]))), + <<"Blah blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+\\1",[]))), + <<"blaH blah">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+\\1",[trim]))), <<"blaH blah">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+\\1",[{parts, - 2}]))), - <<"blaH blah">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+\\1",[]))), - <<":blah">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<"blaH blah">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+\\1",[]))), + <<":blah">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":blah:">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":blah:">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+(?i:\\1)",[]))), - <<":BLAH">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<":blah:">> = iolist_to_binary(join(re:split("blah blah","((?i)blah)\\s+(?i:\\1)",[]))), + <<":BLAH">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":BLAH:">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":BLAH:">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+(?i:\\1)",[]))), - <<":Blah">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<":BLAH:">> = iolist_to_binary(join(re:split("BLAH BLAH","((?i)blah)\\s+(?i:\\1)",[]))), + <<":Blah">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":Blah:">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":Blah:">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+(?i:\\1)",[]))), - <<":blaH">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<":Blah:">> = iolist_to_binary(join(re:split("Blah Blah","((?i)blah)\\s+(?i:\\1)",[]))), + <<":blaH">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":blaH:">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":blaH:">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+(?i:\\1)",[]))), - <<":blah">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<":blaH:">> = iolist_to_binary(join(re:split("blaH blaH","((?i)blah)\\s+(?i:\\1)",[]))), + <<":blah">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":blah:">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":blah:">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+(?i:\\1)",[]))), - <<":Blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<":blah:">> = iolist_to_binary(join(re:split("blah BLAH","((?i)blah)\\s+(?i:\\1)",[]))), + <<":Blah">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":Blah:">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":Blah:">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+(?i:\\1)",[]))), - <<":blaH">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+(?i:\\1)",[trim]))), + 2}]))), + <<":Blah:">> = iolist_to_binary(join(re:split("Blah blah","((?i)blah)\\s+(?i:\\1)",[]))), + <<":blaH">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+(?i:\\1)",[trim]))), <<":blaH:">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+(?i:\\1)",[{parts, - 2}]))), - <<":blaH:">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+(?i:\\1)",[]))), - <<"">> = iolist_to_binary(join(re:split("a","(?>a*)*",[trim]))), + 2}]))), + <<":blaH:">> = iolist_to_binary(join(re:split("blaH blah","((?i)blah)\\s+(?i:\\1)",[]))), + <<"">> = iolist_to_binary(join(re:split("a","(?>a*)*",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","(?>a*)*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","(?>a*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("aa","(?>a*)*",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","(?>a*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("aa","(?>a*)*",[trim]))), <<":">> = iolist_to_binary(join(re:split("aa","(?>a*)*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aa","(?>a*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","(?>a*)*",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aa","(?>a*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","(?>a*)*",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","(?>a*)*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","(?>a*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","(abc|)+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","(?>a*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","(abc|)+",[trim]))), <<"::">> = iolist_to_binary(join(re:split("abc","(abc|)+",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abc","(abc|)+",[]))), - <<"">> = iolist_to_binary(join(re:split("abcabc","(abc|)+",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abc","(abc|)+",[]))), + <<"">> = iolist_to_binary(join(re:split("abcabc","(abc|)+",[trim]))), <<"::">> = iolist_to_binary(join(re:split("abcabc","(abc|)+",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abcabc","(abc|)+",[]))), - <<"">> = iolist_to_binary(join(re:split("abcabcabc","(abc|)+",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abcabc","(abc|)+",[]))), + <<"">> = iolist_to_binary(join(re:split("abcabcabc","(abc|)+",[trim]))), <<"::">> = iolist_to_binary(join(re:split("abcabcabc","(abc|)+",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abcabcabc","(abc|)+",[]))), - <<"x::y::z">> = iolist_to_binary(join(re:split("xyz","(abc|)+",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abcabcabc","(abc|)+",[]))), + <<"x::y::z">> = iolist_to_binary(join(re:split("xyz","(abc|)+",[trim]))), <<"x::yz">> = iolist_to_binary(join(re:split("xyz","(abc|)+",[{parts, - 2}]))), - <<"x::y::z::">> = iolist_to_binary(join(re:split("xyz","(abc|)+",[]))), - <<"">> = iolist_to_binary(join(re:split("a","([a]*)*",[trim]))), + 2}]))), + <<"x::y::z::">> = iolist_to_binary(join(re:split("xyz","(abc|)+",[]))), + <<"">> = iolist_to_binary(join(re:split("a","([a]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","([a]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","([a]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaa","([a]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","([a]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaa","([a]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aaaaa","([a]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaaaa","([a]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("a","([ab]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaaaa","([a]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("a","([ab]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","([ab]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","([ab]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("b","([ab]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","([ab]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("b","([ab]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","([ab]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","([ab]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("ababab","([ab]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","([ab]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("ababab","([ab]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("ababab","([ab]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ababab","([ab]*)*",[]))), - <<"::c::d::e">> = iolist_to_binary(join(re:split("aaaabcde","([ab]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ababab","([ab]*)*",[]))), + <<"::c::d::e">> = iolist_to_binary(join(re:split("aaaabcde","([ab]*)*",[trim]))), <<"::cde">> = iolist_to_binary(join(re:split("aaaabcde","([ab]*)*",[{parts, - 2}]))), - <<"::c::d::e::">> = iolist_to_binary(join(re:split("aaaabcde","([ab]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("bbbb","([ab]*)*",[trim]))), + 2}]))), + <<"::c::d::e::">> = iolist_to_binary(join(re:split("aaaabcde","([ab]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("bbbb","([ab]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("bbbb","([ab]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("bbbb","([ab]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("b","([^a]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("bbbb","([ab]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("b","([^a]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","([^a]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","([^a]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("bbbb","([^a]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","([^a]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("bbbb","([^a]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("bbbb","([^a]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("bbbb","([^a]*)*",[]))), - <<"a::a::a">> = iolist_to_binary(join(re:split("aaa","([^a]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("bbbb","([^a]*)*",[]))), + <<"a::a::a">> = iolist_to_binary(join(re:split("aaa","([^a]*)*",[trim]))), <<"a::aa">> = iolist_to_binary(join(re:split("aaa","([^a]*)*",[{parts, - 2}]))), - <<"a::a::a::">> = iolist_to_binary(join(re:split("aaa","([^a]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("cccc","([^ab]*)*",[trim]))), + 2}]))), + <<"a::a::a::">> = iolist_to_binary(join(re:split("aaa","([^a]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("cccc","([^ab]*)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("cccc","([^ab]*)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("cccc","([^ab]*)*",[]))), - <<"a::b::a::b">> = iolist_to_binary(join(re:split("abab","([^ab]*)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("cccc","([^ab]*)*",[]))), + <<"a::b::a::b">> = iolist_to_binary(join(re:split("abab","([^ab]*)*",[trim]))), <<"a::bab">> = iolist_to_binary(join(re:split("abab","([^ab]*)*",[{parts, - 2}]))), - <<"a::b::a::b::">> = iolist_to_binary(join(re:split("abab","([^ab]*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("a","([a]*?)*",[trim]))), + 2}]))), + <<"a::b::a::b::">> = iolist_to_binary(join(re:split("abab","([^ab]*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("a","([a]*?)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","([a]*?)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","([a]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","([a]*?)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","([a]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","([a]*?)*",[trim]))), <<"::aaa">> = iolist_to_binary(join(re:split("aaaa","([a]*?)*",[{parts, - 2}]))), - <<"::::::::">> = iolist_to_binary(join(re:split("aaaa","([a]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("a","([ab]*?)*",[trim]))), + 2}]))), + <<"::::::::">> = iolist_to_binary(join(re:split("aaaa","([a]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("a","([ab]*?)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","([ab]*?)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","([ab]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("b","([ab]*?)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","([ab]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("b","([ab]*?)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","([ab]*?)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","([ab]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("abab","([ab]*?)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","([ab]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("abab","([ab]*?)*",[trim]))), <<"::bab">> = iolist_to_binary(join(re:split("abab","([ab]*?)*",[{parts, - 2}]))), - <<"::::::::">> = iolist_to_binary(join(re:split("abab","([ab]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("baba","([ab]*?)*",[trim]))), + 2}]))), + <<"::::::::">> = iolist_to_binary(join(re:split("abab","([ab]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("baba","([ab]*?)*",[trim]))), <<"::aba">> = iolist_to_binary(join(re:split("baba","([ab]*?)*",[{parts, - 2}]))), - <<"::::::::">> = iolist_to_binary(join(re:split("baba","([ab]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("b","([^a]*?)*",[trim]))), + 2}]))), + <<"::::::::">> = iolist_to_binary(join(re:split("baba","([ab]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("b","([^a]*?)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","([^a]*?)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","([^a]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("bbbb","([^a]*?)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","([^a]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("bbbb","([^a]*?)*",[trim]))), <<"::bbb">> = iolist_to_binary(join(re:split("bbbb","([^a]*?)*",[{parts, - 2}]))), - <<"::::::::">> = iolist_to_binary(join(re:split("bbbb","([^a]*?)*",[]))), - <<"a::a::a">> = iolist_to_binary(join(re:split("aaa","([^a]*?)*",[trim]))), + 2}]))), + <<"::::::::">> = iolist_to_binary(join(re:split("bbbb","([^a]*?)*",[]))), + <<"a::a::a">> = iolist_to_binary(join(re:split("aaa","([^a]*?)*",[trim]))), <<"a::aa">> = iolist_to_binary(join(re:split("aaa","([^a]*?)*",[{parts, - 2}]))), - <<"a::a::a::">> = iolist_to_binary(join(re:split("aaa","([^a]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("c","([^ab]*?)*",[trim]))), + 2}]))), + <<"a::a::a::">> = iolist_to_binary(join(re:split("aaa","([^a]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("c","([^ab]*?)*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("c","([^ab]*?)*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("c","([^ab]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("cccc","([^ab]*?)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("c","([^ab]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("cccc","([^ab]*?)*",[trim]))), <<"::ccc">> = iolist_to_binary(join(re:split("cccc","([^ab]*?)*",[{parts, - 2}]))), - <<"::::::::">> = iolist_to_binary(join(re:split("cccc","([^ab]*?)*",[]))), - <<"b::a::b::a">> = iolist_to_binary(join(re:split("baba","([^ab]*?)*",[trim]))), + 2}]))), + <<"::::::::">> = iolist_to_binary(join(re:split("cccc","([^ab]*?)*",[]))), + <<"b::a::b::a">> = iolist_to_binary(join(re:split("baba","([^ab]*?)*",[trim]))), <<"b::aba">> = iolist_to_binary(join(re:split("baba","([^ab]*?)*",[{parts, - 2}]))), - <<"b::a::b::a::">> = iolist_to_binary(join(re:split("baba","([^ab]*?)*",[]))), - <<"">> = iolist_to_binary(join(re:split("a","(?>a*)*",[trim]))), + 2}]))), + <<"b::a::b::a::">> = iolist_to_binary(join(re:split("baba","([^ab]*?)*",[]))), + <<"">> = iolist_to_binary(join(re:split("a","(?>a*)*",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","(?>a*)*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","(?>a*)*",[]))), - <<":b:c:d:e">> = iolist_to_binary(join(re:split("aaabcde","(?>a*)*",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","(?>a*)*",[]))), + <<":b:c:d:e">> = iolist_to_binary(join(re:split("aaabcde","(?>a*)*",[trim]))), <<":bcde">> = iolist_to_binary(join(re:split("aaabcde","(?>a*)*",[{parts, - 2}]))), - <<":b:c:d:e:">> = iolist_to_binary(join(re:split("aaabcde","(?>a*)*",[]))), + 2}]))), + <<":b:c:d:e:">> = iolist_to_binary(join(re:split("aaabcde","(?>a*)*",[]))), ok. run14() -> - <<"">> = iolist_to_binary(join(re:split("aaaaa","((?>a*))*",[trim]))), + <<"">> = iolist_to_binary(join(re:split("aaaaa","((?>a*))*",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aaaaa","((?>a*))*",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaaaa","((?>a*))*",[]))), - <<"::b::b">> = iolist_to_binary(join(re:split("aabbaa","((?>a*))*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaaaa","((?>a*))*",[]))), + <<"::b::b">> = iolist_to_binary(join(re:split("aabbaa","((?>a*))*",[trim]))), <<"::bbaa">> = iolist_to_binary(join(re:split("aabbaa","((?>a*))*",[{parts, - 2}]))), - <<"::b::b::">> = iolist_to_binary(join(re:split("aabbaa","((?>a*))*",[]))), - <<"a::a::a::a::a">> = iolist_to_binary(join(re:split("aaaaa","((?>a*?))*",[trim]))), + 2}]))), + <<"::b::b::">> = iolist_to_binary(join(re:split("aabbaa","((?>a*))*",[]))), + <<"a::a::a::a::a">> = iolist_to_binary(join(re:split("aaaaa","((?>a*?))*",[trim]))), <<"a::aaaa">> = iolist_to_binary(join(re:split("aaaaa","((?>a*?))*",[{parts, - 2}]))), - <<"a::a::a::a::a::">> = iolist_to_binary(join(re:split("aaaaa","((?>a*?))*",[]))), - <<"a::a::b::b::a::a">> = iolist_to_binary(join(re:split("aabbaa","((?>a*?))*",[trim]))), + 2}]))), + <<"a::a::a::a::a::">> = iolist_to_binary(join(re:split("aaaaa","((?>a*?))*",[]))), + <<"a::a::b::b::a::a">> = iolist_to_binary(join(re:split("aabbaa","((?>a*?))*",[trim]))), <<"a::abbaa">> = iolist_to_binary(join(re:split("aabbaa","((?>a*?))*",[{parts, - 2}]))), - <<"a::a::b::b::a::a::">> = iolist_to_binary(join(re:split("aabbaa","((?>a*?))*",[]))), + 2}]))), + <<"a::a::b::b::a::a::">> = iolist_to_binary(join(re:split("aabbaa","((?>a*?))*",[]))), <<"">> = iolist_to_binary(join(re:split("12-sep-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("12-sep-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12-sep-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12-sep-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), <<"">> = iolist_to_binary(join(re:split("12-09-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("12-09-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("12-09-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("12-09-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), <<"sep-12-98">> = iolist_to_binary(join(re:split("sep-12-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, - trim]))), + trim]))), <<"sep-12-98">> = iolist_to_binary(join(re:split("sep-12-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended, {parts, - 2}]))), - <<"sep-12-98">> = iolist_to_binary(join(re:split("sep-12-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), - <<"foo:foo">> = iolist_to_binary(join(re:split("foobarfoo","(?<=(foo))bar\\1",[trim]))), + 2}]))), + <<"sep-12-98">> = iolist_to_binary(join(re:split("sep-12-98","(?(?=[^a-z]+[a-z]) \\d{2}-[a-z]{3}-\\d{2} | \\d{2}-\\d{2}-\\d{2} ) ",[extended]))), + <<"foo:foo">> = iolist_to_binary(join(re:split("foobarfoo","(?<=(foo))bar\\1",[trim]))), <<"foo:foo:">> = iolist_to_binary(join(re:split("foobarfoo","(?<=(foo))bar\\1",[{parts, - 2}]))), - <<"foo:foo:">> = iolist_to_binary(join(re:split("foobarfoo","(?<=(foo))bar\\1",[]))), - <<"foo:foo:tling">> = iolist_to_binary(join(re:split("foobarfootling","(?<=(foo))bar\\1",[trim]))), + 2}]))), + <<"foo:foo:">> = iolist_to_binary(join(re:split("foobarfoo","(?<=(foo))bar\\1",[]))), + <<"foo:foo:tling">> = iolist_to_binary(join(re:split("foobarfootling","(?<=(foo))bar\\1",[trim]))), <<"foo:foo:tling">> = iolist_to_binary(join(re:split("foobarfootling","(?<=(foo))bar\\1",[{parts, - 2}]))), - <<"foo:foo:tling">> = iolist_to_binary(join(re:split("foobarfootling","(?<=(foo))bar\\1",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo))bar\\1",[trim]))), + 2}]))), + <<"foo:foo:tling">> = iolist_to_binary(join(re:split("foobarfootling","(?<=(foo))bar\\1",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo))bar\\1",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo))bar\\1",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo))bar\\1",[]))), - <<"foobar">> = iolist_to_binary(join(re:split("foobar","(?<=(foo))bar\\1",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(foo))bar\\1",[]))), + <<"foobar">> = iolist_to_binary(join(re:split("foobar","(?<=(foo))bar\\1",[trim]))), <<"foobar">> = iolist_to_binary(join(re:split("foobar","(?<=(foo))bar\\1",[{parts, - 2}]))), - <<"foobar">> = iolist_to_binary(join(re:split("foobar","(?<=(foo))bar\\1",[]))), - <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<=(foo))bar\\1",[trim]))), + 2}]))), + <<"foobar">> = iolist_to_binary(join(re:split("foobar","(?<=(foo))bar\\1",[]))), + <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<=(foo))bar\\1",[trim]))), <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<=(foo))bar\\1",[{parts, - 2}]))), - <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<=(foo))bar\\1",[]))), - <<"">> = iolist_to_binary(join(re:split("saturday","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<"barfoo">> = iolist_to_binary(join(re:split("barfoo","(?<=(foo))bar\\1",[]))), + <<"">> = iolist_to_binary(join(re:split("saturday","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("saturday","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("saturday","(?i:saturday|sunday)",[]))), - <<"">> = iolist_to_binary(join(re:split("sunday","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("saturday","(?i:saturday|sunday)",[]))), + <<"">> = iolist_to_binary(join(re:split("sunday","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("sunday","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("sunday","(?i:saturday|sunday)",[]))), - <<"">> = iolist_to_binary(join(re:split("Saturday","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("sunday","(?i:saturday|sunday)",[]))), + <<"">> = iolist_to_binary(join(re:split("Saturday","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("Saturday","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("Saturday","(?i:saturday|sunday)",[]))), - <<"">> = iolist_to_binary(join(re:split("Sunday","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("Saturday","(?i:saturday|sunday)",[]))), + <<"">> = iolist_to_binary(join(re:split("Sunday","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("Sunday","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("Sunday","(?i:saturday|sunday)",[]))), - <<"">> = iolist_to_binary(join(re:split("SATURDAY","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("Sunday","(?i:saturday|sunday)",[]))), + <<"">> = iolist_to_binary(join(re:split("SATURDAY","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("SATURDAY","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("SATURDAY","(?i:saturday|sunday)",[]))), - <<"">> = iolist_to_binary(join(re:split("SUNDAY","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("SATURDAY","(?i:saturday|sunday)",[]))), + <<"">> = iolist_to_binary(join(re:split("SUNDAY","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("SUNDAY","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("SUNDAY","(?i:saturday|sunday)",[]))), - <<"">> = iolist_to_binary(join(re:split("SunDay","(?i:saturday|sunday)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("SUNDAY","(?i:saturday|sunday)",[]))), + <<"">> = iolist_to_binary(join(re:split("SunDay","(?i:saturday|sunday)",[trim]))), <<":">> = iolist_to_binary(join(re:split("SunDay","(?i:saturday|sunday)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("SunDay","(?i:saturday|sunday)",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcx","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("SunDay","(?i:saturday|sunday)",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcx","(a(?i)bc|BB)x",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcx","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcx","(a(?i)bc|BB)x",[]))), - <<":aBC">> = iolist_to_binary(join(re:split("aBCx","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcx","(a(?i)bc|BB)x",[]))), + <<":aBC">> = iolist_to_binary(join(re:split("aBCx","(a(?i)bc|BB)x",[trim]))), <<":aBC:">> = iolist_to_binary(join(re:split("aBCx","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<":aBC:">> = iolist_to_binary(join(re:split("aBCx","(a(?i)bc|BB)x",[]))), - <<":bb">> = iolist_to_binary(join(re:split("bbx","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<":aBC:">> = iolist_to_binary(join(re:split("aBCx","(a(?i)bc|BB)x",[]))), + <<":bb">> = iolist_to_binary(join(re:split("bbx","(a(?i)bc|BB)x",[trim]))), <<":bb:">> = iolist_to_binary(join(re:split("bbx","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<":bb:">> = iolist_to_binary(join(re:split("bbx","(a(?i)bc|BB)x",[]))), - <<":BB">> = iolist_to_binary(join(re:split("BBx","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<":bb:">> = iolist_to_binary(join(re:split("bbx","(a(?i)bc|BB)x",[]))), + <<":BB">> = iolist_to_binary(join(re:split("BBx","(a(?i)bc|BB)x",[trim]))), <<":BB:">> = iolist_to_binary(join(re:split("BBx","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<":BB:">> = iolist_to_binary(join(re:split("BBx","(a(?i)bc|BB)x",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<":BB:">> = iolist_to_binary(join(re:split("BBx","(a(?i)bc|BB)x",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)bc|BB)x",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)bc|BB)x",[]))), - <<"abcX">> = iolist_to_binary(join(re:split("abcX","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(a(?i)bc|BB)x",[]))), + <<"abcX">> = iolist_to_binary(join(re:split("abcX","(a(?i)bc|BB)x",[trim]))), <<"abcX">> = iolist_to_binary(join(re:split("abcX","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<"abcX">> = iolist_to_binary(join(re:split("abcX","(a(?i)bc|BB)x",[]))), - <<"aBCX">> = iolist_to_binary(join(re:split("aBCX","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<"abcX">> = iolist_to_binary(join(re:split("abcX","(a(?i)bc|BB)x",[]))), + <<"aBCX">> = iolist_to_binary(join(re:split("aBCX","(a(?i)bc|BB)x",[trim]))), <<"aBCX">> = iolist_to_binary(join(re:split("aBCX","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<"aBCX">> = iolist_to_binary(join(re:split("aBCX","(a(?i)bc|BB)x",[]))), - <<"bbX">> = iolist_to_binary(join(re:split("bbX","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<"aBCX">> = iolist_to_binary(join(re:split("aBCX","(a(?i)bc|BB)x",[]))), + <<"bbX">> = iolist_to_binary(join(re:split("bbX","(a(?i)bc|BB)x",[trim]))), <<"bbX">> = iolist_to_binary(join(re:split("bbX","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<"bbX">> = iolist_to_binary(join(re:split("bbX","(a(?i)bc|BB)x",[]))), - <<"BBX">> = iolist_to_binary(join(re:split("BBX","(a(?i)bc|BB)x",[trim]))), + 2}]))), + <<"bbX">> = iolist_to_binary(join(re:split("bbX","(a(?i)bc|BB)x",[]))), + <<"BBX">> = iolist_to_binary(join(re:split("BBX","(a(?i)bc|BB)x",[trim]))), <<"BBX">> = iolist_to_binary(join(re:split("BBX","(a(?i)bc|BB)x",[{parts, - 2}]))), - <<"BBX">> = iolist_to_binary(join(re:split("BBX","(a(?i)bc|BB)x",[]))), - <<":ac">> = iolist_to_binary(join(re:split("ac","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<"BBX">> = iolist_to_binary(join(re:split("BBX","(a(?i)bc|BB)x",[]))), + <<":ac">> = iolist_to_binary(join(re:split("ac","^([ab](?i)[cd]|[ef])",[trim]))), <<":ac:">> = iolist_to_binary(join(re:split("ac","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":ac:">> = iolist_to_binary(join(re:split("ac","^([ab](?i)[cd]|[ef])",[]))), - <<":aC">> = iolist_to_binary(join(re:split("aC","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":ac:">> = iolist_to_binary(join(re:split("ac","^([ab](?i)[cd]|[ef])",[]))), + <<":aC">> = iolist_to_binary(join(re:split("aC","^([ab](?i)[cd]|[ef])",[trim]))), <<":aC:">> = iolist_to_binary(join(re:split("aC","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":aC:">> = iolist_to_binary(join(re:split("aC","^([ab](?i)[cd]|[ef])",[]))), - <<":bD">> = iolist_to_binary(join(re:split("bD","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":aC:">> = iolist_to_binary(join(re:split("aC","^([ab](?i)[cd]|[ef])",[]))), + <<":bD">> = iolist_to_binary(join(re:split("bD","^([ab](?i)[cd]|[ef])",[trim]))), <<":bD:">> = iolist_to_binary(join(re:split("bD","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":bD:">> = iolist_to_binary(join(re:split("bD","^([ab](?i)[cd]|[ef])",[]))), - <<":e:lephant">> = iolist_to_binary(join(re:split("elephant","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":bD:">> = iolist_to_binary(join(re:split("bD","^([ab](?i)[cd]|[ef])",[]))), + <<":e:lephant">> = iolist_to_binary(join(re:split("elephant","^([ab](?i)[cd]|[ef])",[trim]))), <<":e:lephant">> = iolist_to_binary(join(re:split("elephant","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":e:lephant">> = iolist_to_binary(join(re:split("elephant","^([ab](?i)[cd]|[ef])",[]))), - <<":E:urope">> = iolist_to_binary(join(re:split("Europe","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":e:lephant">> = iolist_to_binary(join(re:split("elephant","^([ab](?i)[cd]|[ef])",[]))), + <<":E:urope">> = iolist_to_binary(join(re:split("Europe","^([ab](?i)[cd]|[ef])",[trim]))), <<":E:urope">> = iolist_to_binary(join(re:split("Europe","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":E:urope">> = iolist_to_binary(join(re:split("Europe","^([ab](?i)[cd]|[ef])",[]))), - <<":f:rog">> = iolist_to_binary(join(re:split("frog","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":E:urope">> = iolist_to_binary(join(re:split("Europe","^([ab](?i)[cd]|[ef])",[]))), + <<":f:rog">> = iolist_to_binary(join(re:split("frog","^([ab](?i)[cd]|[ef])",[trim]))), <<":f:rog">> = iolist_to_binary(join(re:split("frog","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":f:rog">> = iolist_to_binary(join(re:split("frog","^([ab](?i)[cd]|[ef])",[]))), - <<":F:rance">> = iolist_to_binary(join(re:split("France","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":f:rog">> = iolist_to_binary(join(re:split("frog","^([ab](?i)[cd]|[ef])",[]))), + <<":F:rance">> = iolist_to_binary(join(re:split("France","^([ab](?i)[cd]|[ef])",[trim]))), <<":F:rance">> = iolist_to_binary(join(re:split("France","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<":F:rance">> = iolist_to_binary(join(re:split("France","^([ab](?i)[cd]|[ef])",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<":F:rance">> = iolist_to_binary(join(re:split("France","^([ab](?i)[cd]|[ef])",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([ab](?i)[cd]|[ef])",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([ab](?i)[cd]|[ef])",[]))), - <<"Africa">> = iolist_to_binary(join(re:split("Africa","^([ab](?i)[cd]|[ef])",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^([ab](?i)[cd]|[ef])",[]))), + <<"Africa">> = iolist_to_binary(join(re:split("Africa","^([ab](?i)[cd]|[ef])",[trim]))), <<"Africa">> = iolist_to_binary(join(re:split("Africa","^([ab](?i)[cd]|[ef])",[{parts, - 2}]))), - <<"Africa">> = iolist_to_binary(join(re:split("Africa","^([ab](?i)[cd]|[ef])",[]))), - <<":ab">> = iolist_to_binary(join(re:split("ab","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<"Africa">> = iolist_to_binary(join(re:split("Africa","^([ab](?i)[cd]|[ef])",[]))), + <<":ab">> = iolist_to_binary(join(re:split("ab","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<":ab:">> = iolist_to_binary(join(re:split("ab","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("ab","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<":aBd">> = iolist_to_binary(join(re:split("aBd","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("ab","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<":aBd">> = iolist_to_binary(join(re:split("aBd","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<":aBd:">> = iolist_to_binary(join(re:split("aBd","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<":aBd:">> = iolist_to_binary(join(re:split("aBd","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<":xy">> = iolist_to_binary(join(re:split("xy","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<":aBd:">> = iolist_to_binary(join(re:split("aBd","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<":xy">> = iolist_to_binary(join(re:split("xy","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<":xy:">> = iolist_to_binary(join(re:split("xy","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<":xy:">> = iolist_to_binary(join(re:split("xy","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<":xY">> = iolist_to_binary(join(re:split("xY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<":xy:">> = iolist_to_binary(join(re:split("xy","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<":xY">> = iolist_to_binary(join(re:split("xY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<":xY:">> = iolist_to_binary(join(re:split("xY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<":xY:">> = iolist_to_binary(join(re:split("xY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<":z:ebra">> = iolist_to_binary(join(re:split("zebra","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<":xY:">> = iolist_to_binary(join(re:split("xY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<":z:ebra">> = iolist_to_binary(join(re:split("zebra","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<":z:ebra">> = iolist_to_binary(join(re:split("zebra","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<":z:ebra">> = iolist_to_binary(join(re:split("zebra","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<":Z:ambesi">> = iolist_to_binary(join(re:split("Zambesi","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<":z:ebra">> = iolist_to_binary(join(re:split("zebra","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<":Z:ambesi">> = iolist_to_binary(join(re:split("Zambesi","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<":Z:ambesi">> = iolist_to_binary(join(re:split("Zambesi","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<":Z:ambesi">> = iolist_to_binary(join(re:split("Zambesi","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<":Z:ambesi">> = iolist_to_binary(join(re:split("Zambesi","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<"aCD">> = iolist_to_binary(join(re:split("aCD","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<"aCD">> = iolist_to_binary(join(re:split("aCD","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<"aCD">> = iolist_to_binary(join(re:split("aCD","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<"aCD">> = iolist_to_binary(join(re:split("aCD","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), - <<"XY">> = iolist_to_binary(join(re:split("XY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), + 2}]))), + <<"aCD">> = iolist_to_binary(join(re:split("aCD","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + <<"XY">> = iolist_to_binary(join(re:split("XY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[trim]))), <<"XY">> = iolist_to_binary(join(re:split("XY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[{parts, - 2}]))), - <<"XY">> = iolist_to_binary(join(re:split("XY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), + 2}]))), + <<"XY">> = iolist_to_binary(join(re:split("XY","^(ab|a(?i)[b-c](?m-i)d|x(?i)y|z)",[]))), <<"foo ">> = iolist_to_binary(join(re:split("foo -bar","(?<=foo\\n)^bar",[multiline,trim]))), +bar","(?<=foo\\n)^bar",[multiline,trim]))), <<"foo :">> = iolist_to_binary(join(re:split("foo -bar","(?<=foo\\n)^bar",[multiline,{parts,2}]))), +bar","(?<=foo\\n)^bar",[multiline,{parts,2}]))), <<"foo :">> = iolist_to_binary(join(re:split("foo -bar","(?<=foo\\n)^bar",[multiline]))), +bar","(?<=foo\\n)^bar",[multiline]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=foo\\n)^bar",[multiline, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=foo\\n)^bar",[multiline, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=foo\\n)^bar",[multiline]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=foo\\n)^bar",[multiline]))), <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=foo\\n)^bar",[multiline, - trim]))), + trim]))), <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=foo\\n)^bar",[multiline, {parts, - 2}]))), - <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=foo\\n)^bar",[multiline]))), + 2}]))), + <<"bar">> = iolist_to_binary(join(re:split("bar","(?<=foo\\n)^bar",[multiline]))), <<"baz bar">> = iolist_to_binary(join(re:split("baz -bar","(?<=foo\\n)^bar",[multiline,trim]))), +bar","(?<=foo\\n)^bar",[multiline,trim]))), <<"baz bar">> = iolist_to_binary(join(re:split("baz -bar","(?<=foo\\n)^bar",[multiline,{parts,2}]))), +bar","(?<=foo\\n)^bar",[multiline,{parts,2}]))), <<"baz bar">> = iolist_to_binary(join(re:split("baz -bar","(?<=foo\\n)^bar",[multiline]))), - <<"bar">> = iolist_to_binary(join(re:split("barbaz","(?<=(?<!foo)bar)baz",[trim]))), +bar","(?<=foo\\n)^bar",[multiline]))), + <<"bar">> = iolist_to_binary(join(re:split("barbaz","(?<=(?<!foo)bar)baz",[trim]))), <<"bar:">> = iolist_to_binary(join(re:split("barbaz","(?<=(?<!foo)bar)baz",[{parts, - 2}]))), - <<"bar:">> = iolist_to_binary(join(re:split("barbaz","(?<=(?<!foo)bar)baz",[]))), - <<"barbar">> = iolist_to_binary(join(re:split("barbarbaz","(?<=(?<!foo)bar)baz",[trim]))), + 2}]))), + <<"bar:">> = iolist_to_binary(join(re:split("barbaz","(?<=(?<!foo)bar)baz",[]))), + <<"barbar">> = iolist_to_binary(join(re:split("barbarbaz","(?<=(?<!foo)bar)baz",[trim]))), <<"barbar:">> = iolist_to_binary(join(re:split("barbarbaz","(?<=(?<!foo)bar)baz",[{parts, - 2}]))), - <<"barbar:">> = iolist_to_binary(join(re:split("barbarbaz","(?<=(?<!foo)bar)baz",[]))), - <<"koobar">> = iolist_to_binary(join(re:split("koobarbaz","(?<=(?<!foo)bar)baz",[trim]))), + 2}]))), + <<"barbar:">> = iolist_to_binary(join(re:split("barbarbaz","(?<=(?<!foo)bar)baz",[]))), + <<"koobar">> = iolist_to_binary(join(re:split("koobarbaz","(?<=(?<!foo)bar)baz",[trim]))), <<"koobar:">> = iolist_to_binary(join(re:split("koobarbaz","(?<=(?<!foo)bar)baz",[{parts, - 2}]))), - <<"koobar:">> = iolist_to_binary(join(re:split("koobarbaz","(?<=(?<!foo)bar)baz",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?<!foo)bar)baz",[trim]))), + 2}]))), + <<"koobar:">> = iolist_to_binary(join(re:split("koobarbaz","(?<=(?<!foo)bar)baz",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?<!foo)bar)baz",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?<!foo)bar)baz",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?<!foo)bar)baz",[]))), - <<"baz">> = iolist_to_binary(join(re:split("baz","(?<=(?<!foo)bar)baz",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?<!foo)bar)baz",[]))), + <<"baz">> = iolist_to_binary(join(re:split("baz","(?<=(?<!foo)bar)baz",[trim]))), <<"baz">> = iolist_to_binary(join(re:split("baz","(?<=(?<!foo)bar)baz",[{parts, - 2}]))), - <<"baz">> = iolist_to_binary(join(re:split("baz","(?<=(?<!foo)bar)baz",[]))), - <<"foobarbaz">> = iolist_to_binary(join(re:split("foobarbaz","(?<=(?<!foo)bar)baz",[trim]))), + 2}]))), + <<"baz">> = iolist_to_binary(join(re:split("baz","(?<=(?<!foo)bar)baz",[]))), + <<"foobarbaz">> = iolist_to_binary(join(re:split("foobarbaz","(?<=(?<!foo)bar)baz",[trim]))), <<"foobarbaz">> = iolist_to_binary(join(re:split("foobarbaz","(?<=(?<!foo)bar)baz",[{parts, - 2}]))), - <<"foobarbaz">> = iolist_to_binary(join(re:split("foobarbaz","(?<=(?<!foo)bar)baz",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"foobarbaz">> = iolist_to_binary(join(re:split("foobarbaz","(?<=(?<!foo)bar)baz",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?){4}$",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?){4}$",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?){4}$",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?){4}$",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?){4}$",[trim]))), <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?){4}$",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?){4}$",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?){4}$",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?){4}$",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?){4}$",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?){4}$",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?){4}$",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?){4}$",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?){4}$",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[]))), - <<":aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[]))), + <<":aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<":a:a:a:a">> = iolist_to_binary(join(re:split("aaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<":a:a:a:a">> = iolist_to_binary(join(re:split("aaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<":a:a:a:a:">> = iolist_to_binary(join(re:split("aaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<":a:a:a:a:">> = iolist_to_binary(join(re:split("aaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<":a:aa:a:a">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<":a:a:a:a:">> = iolist_to_binary(join(re:split("aaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<":a:aa:a:a">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<":a:aa:a:a:">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<":a:aa:a:a:">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<":a:aa:a:aa">> = iolist_to_binary(join(re:split("aaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<":a:aa:a:a:">> = iolist_to_binary(join(re:split("aaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<":a:aa:a:aa">> = iolist_to_binary(join(re:split("aaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<":a:aa:a:aa:">> = iolist_to_binary(join(re:split("aaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<":a:aa:a:aa:">> = iolist_to_binary(join(re:split("aaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<":a:aa:aaa:a">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<":a:aa:a:aa:">> = iolist_to_binary(join(re:split("aaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<":a:aa:aaa:a">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<":a:aa:aaa:a:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<":a:aa:aaa:a:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<":a:aa:aaa:a:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<":a:aa:aaa:aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<":a:aa:aaa:aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<":a:aa:aaa:aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<":a:aa:aaa:aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<":a:aa:aaa:aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[trim]))), <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","abc",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaa","^(a\\1?)(a\\1?)(a\\2?)(a\\3?)$",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","abc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","abc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","abc",[]))), - <<"x:y">> = iolist_to_binary(join(re:split("xabcy","abc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","abc",[]))), + <<"x:y">> = iolist_to_binary(join(re:split("xabcy","abc",[trim]))), <<"x:y">> = iolist_to_binary(join(re:split("xabcy","abc",[{parts, - 2}]))), - <<"x:y">> = iolist_to_binary(join(re:split("xabcy","abc",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ababc","abc",[trim]))), + 2}]))), + <<"x:y">> = iolist_to_binary(join(re:split("xabcy","abc",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ababc","abc",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("ababc","abc",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("ababc","abc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("ababc","abc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[]))), - <<"xbc">> = iolist_to_binary(join(re:split("xbc","abc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[]))), + <<"xbc">> = iolist_to_binary(join(re:split("xbc","abc",[trim]))), <<"xbc">> = iolist_to_binary(join(re:split("xbc","abc",[{parts, - 2}]))), - <<"xbc">> = iolist_to_binary(join(re:split("xbc","abc",[]))), - <<"axc">> = iolist_to_binary(join(re:split("axc","abc",[trim]))), + 2}]))), + <<"xbc">> = iolist_to_binary(join(re:split("xbc","abc",[]))), + <<"axc">> = iolist_to_binary(join(re:split("axc","abc",[trim]))), <<"axc">> = iolist_to_binary(join(re:split("axc","abc",[{parts, - 2}]))), - <<"axc">> = iolist_to_binary(join(re:split("axc","abc",[]))), - <<"abx">> = iolist_to_binary(join(re:split("abx","abc",[trim]))), + 2}]))), + <<"axc">> = iolist_to_binary(join(re:split("axc","abc",[]))), + <<"abx">> = iolist_to_binary(join(re:split("abx","abc",[trim]))), <<"abx">> = iolist_to_binary(join(re:split("abx","abc",[{parts, - 2}]))), - <<"abx">> = iolist_to_binary(join(re:split("abx","abc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","ab*c",[trim]))), + 2}]))), + <<"abx">> = iolist_to_binary(join(re:split("abx","abc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","ab*c",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","ab*c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","ab*c",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","ab*bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","ab*c",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","ab*bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","ab*bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","ab*bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbc","ab*bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","ab*bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbc","ab*bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbc","ab*bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbc","ab*bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab*bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbc","ab*bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab*bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab*bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab*bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc",".{1}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab*bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc",".{1}",[trim]))), <<":bbbbc">> = iolist_to_binary(join(re:split("abbbbc",".{1}",[{parts, - 2}]))), - <<"::::::">> = iolist_to_binary(join(re:split("abbbbc",".{1}",[]))), - <<":bc">> = iolist_to_binary(join(re:split("abbbbc",".{3,4}",[trim]))), + 2}]))), + <<"::::::">> = iolist_to_binary(join(re:split("abbbbc",".{1}",[]))), + <<":bc">> = iolist_to_binary(join(re:split("abbbbc",".{3,4}",[trim]))), <<":bc">> = iolist_to_binary(join(re:split("abbbbc",".{3,4}",[{parts, - 2}]))), - <<":bc">> = iolist_to_binary(join(re:split("abbbbc",".{3,4}",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{0,}bc",[trim]))), + 2}]))), + <<":bc">> = iolist_to_binary(join(re:split("abbbbc",".{3,4}",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{0,}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{0,}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{0,}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbc","ab+bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{0,}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbc","ab+bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbc","ab+bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbc","ab+bc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbc","ab+bc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","ab+bc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","ab+bc",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","ab+bc",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","ab+bc",[]))), - <<"abq">> = iolist_to_binary(join(re:split("abq","ab+bc",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","ab+bc",[]))), + <<"abq">> = iolist_to_binary(join(re:split("abq","ab+bc",[trim]))), <<"abq">> = iolist_to_binary(join(re:split("abq","ab+bc",[{parts, - 2}]))), - <<"abq">> = iolist_to_binary(join(re:split("abq","ab+bc",[]))), + 2}]))), + <<"abq">> = iolist_to_binary(join(re:split("abq","ab+bc",[]))), ok. run15() -> - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab+bc",[trim]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab+bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab+bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab+bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{1,}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab+bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{1,}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{3,4}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{1,3}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbbbc","ab{3,4}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{3,4}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{3,4}bc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbbbc","ab{3,4}bc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}bc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}bc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}bc",[]))), - <<"abq">> = iolist_to_binary(join(re:split("abq","ab{4,5}bc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}bc",[]))), + <<"abq">> = iolist_to_binary(join(re:split("abq","ab{4,5}bc",[trim]))), <<"abq">> = iolist_to_binary(join(re:split("abq","ab{4,5}bc",[{parts, - 2}]))), - <<"abq">> = iolist_to_binary(join(re:split("abq","ab{4,5}bc",[]))), - <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","ab{4,5}bc",[trim]))), + 2}]))), + <<"abq">> = iolist_to_binary(join(re:split("abq","ab{4,5}bc",[]))), + <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","ab{4,5}bc",[trim]))), <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","ab{4,5}bc",[{parts, - 2}]))), - <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","ab{4,5}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abbc","ab?bc",[trim]))), + 2}]))), + <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","ab{4,5}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abbc","ab?bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abbc","ab?bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abbc","ab?bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","ab?bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abbc","ab?bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","ab?bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","ab?bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","ab?bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","ab{0,1}bc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","ab?bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","ab{0,1}bc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","ab{0,1}bc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","ab{0,1}bc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","ab?c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","ab{0,1}bc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","ab?c",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","ab?c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","ab?c",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","ab{0,1}c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","ab?c",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","ab{0,1}c",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","ab{0,1}c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","ab{0,1}c",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","^abc$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","ab{0,1}c",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","^abc$",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","^abc$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[]))), - <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","^abc$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[]))), + <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","^abc$",[trim]))), <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","^abc$",[{parts, - 2}]))), - <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","^abc$",[]))), - <<"abcc">> = iolist_to_binary(join(re:split("abcc","^abc$",[trim]))), + 2}]))), + <<"abbbbc">> = iolist_to_binary(join(re:split("abbbbc","^abc$",[]))), + <<"abcc">> = iolist_to_binary(join(re:split("abcc","^abc$",[trim]))), <<"abcc">> = iolist_to_binary(join(re:split("abcc","^abc$",[{parts, - 2}]))), - <<"abcc">> = iolist_to_binary(join(re:split("abcc","^abc$",[]))), - <<":c">> = iolist_to_binary(join(re:split("abcc","^abc",[trim]))), + 2}]))), + <<"abcc">> = iolist_to_binary(join(re:split("abcc","^abc$",[]))), + <<":c">> = iolist_to_binary(join(re:split("abcc","^abc",[trim]))), <<":c">> = iolist_to_binary(join(re:split("abcc","^abc",[{parts, - 2}]))), - <<":c">> = iolist_to_binary(join(re:split("abcc","^abc",[]))), - <<"a">> = iolist_to_binary(join(re:split("aabc","abc$",[trim]))), + 2}]))), + <<":c">> = iolist_to_binary(join(re:split("abcc","^abc",[]))), + <<"a">> = iolist_to_binary(join(re:split("aabc","abc$",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aabc","abc$",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aabc","abc$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aabc","abc$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[]))), - <<"a">> = iolist_to_binary(join(re:split("aabc","abc$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc$",[]))), + <<"a">> = iolist_to_binary(join(re:split("aabc","abc$",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aabc","abc$",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aabc","abc$",[]))), - <<"aabcd">> = iolist_to_binary(join(re:split("aabcd","abc$",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aabc","abc$",[]))), + <<"aabcd">> = iolist_to_binary(join(re:split("aabcd","abc$",[trim]))), <<"aabcd">> = iolist_to_binary(join(re:split("aabcd","abc$",[{parts, - 2}]))), - <<"aabcd">> = iolist_to_binary(join(re:split("aabcd","abc$",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^",[trim]))), + 2}]))), + <<"aabcd">> = iolist_to_binary(join(re:split("aabcd","abc$",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","$",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","$",[trim]))), <<"abc:">> = iolist_to_binary(join(re:split("abc","$",[{parts, - 2}]))), - <<"abc:">> = iolist_to_binary(join(re:split("abc","$",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","a.c",[trim]))), + 2}]))), + <<"abc:">> = iolist_to_binary(join(re:split("abc","$",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","a.c",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","a.c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","a.c",[]))), - <<"">> = iolist_to_binary(join(re:split("axc","a.c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","a.c",[]))), + <<"">> = iolist_to_binary(join(re:split("axc","a.c",[trim]))), <<":">> = iolist_to_binary(join(re:split("axc","a.c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("axc","a.c",[]))), - <<"">> = iolist_to_binary(join(re:split("axyzc","a.*c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("axc","a.c",[]))), + <<"">> = iolist_to_binary(join(re:split("axyzc","a.*c",[trim]))), <<":">> = iolist_to_binary(join(re:split("axyzc","a.*c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("axyzc","a.*c",[]))), - <<"">> = iolist_to_binary(join(re:split("abd","a[bc]d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("axyzc","a.*c",[]))), + <<"">> = iolist_to_binary(join(re:split("abd","a[bc]d",[trim]))), <<":">> = iolist_to_binary(join(re:split("abd","a[bc]d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abd","a[bc]d",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bc]d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abd","a[bc]d",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bc]d",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bc]d",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bc]d",[]))), - <<"axyzd">> = iolist_to_binary(join(re:split("axyzd","a[bc]d",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bc]d",[]))), + <<"axyzd">> = iolist_to_binary(join(re:split("axyzd","a[bc]d",[trim]))), <<"axyzd">> = iolist_to_binary(join(re:split("axyzd","a[bc]d",[{parts, - 2}]))), - <<"axyzd">> = iolist_to_binary(join(re:split("axyzd","a[bc]d",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","a[bc]d",[trim]))), + 2}]))), + <<"axyzd">> = iolist_to_binary(join(re:split("axyzd","a[bc]d",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","a[bc]d",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","a[bc]d",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","a[bc]d",[]))), - <<"">> = iolist_to_binary(join(re:split("ace","a[b-d]e",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","a[bc]d",[]))), + <<"">> = iolist_to_binary(join(re:split("ace","a[b-d]e",[trim]))), <<":">> = iolist_to_binary(join(re:split("ace","a[b-d]e",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ace","a[b-d]e",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ace","a[b-d]e",[]))), ok. run16() -> - <<"a">> = iolist_to_binary(join(re:split("aac","a[b-d]",[trim]))), + <<"a">> = iolist_to_binary(join(re:split("aac","a[b-d]",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aac","a[b-d]",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aac","a[b-d]",[]))), - <<"">> = iolist_to_binary(join(re:split("a-","a[-b]",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aac","a[b-d]",[]))), + <<"">> = iolist_to_binary(join(re:split("a-","a[-b]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-","a[-b]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-","a[-b]",[]))), - <<"">> = iolist_to_binary(join(re:split("a-","a[b-]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-","a[-b]",[]))), + <<"">> = iolist_to_binary(join(re:split("a-","a[b-]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-","a[b-]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-","a[b-]",[]))), - <<"">> = iolist_to_binary(join(re:split("a]","a]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-","a[b-]",[]))), + <<"">> = iolist_to_binary(join(re:split("a]","a]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a]","a]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a]","a]",[]))), - <<"">> = iolist_to_binary(join(re:split("a]b","a[]]b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a]","a]",[]))), + <<"">> = iolist_to_binary(join(re:split("a]b","a[]]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a]b","a[]]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a]b","a[]]b",[]))), - <<"">> = iolist_to_binary(join(re:split("aed","a[^bc]d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a]b","a[]]b",[]))), + <<"">> = iolist_to_binary(join(re:split("aed","a[^bc]d",[trim]))), <<":">> = iolist_to_binary(join(re:split("aed","a[^bc]d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aed","a[^bc]d",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^bc]d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aed","a[^bc]d",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^bc]d",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^bc]d",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^bc]d",[]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^bc]d",[]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[trim]))), <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[{parts, - 2}]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[trim]))), + 2}]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[trim]))), <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[{parts, - 2}]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[]))), - <<"">> = iolist_to_binary(join(re:split("adc","a[^-b]c",[trim]))), + 2}]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","a[^bc]d",[]))), + <<"">> = iolist_to_binary(join(re:split("adc","a[^-b]c",[trim]))), <<":">> = iolist_to_binary(join(re:split("adc","a[^-b]c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("adc","a[^-b]c",[]))), - <<"">> = iolist_to_binary(join(re:split("adc","a[^]b]c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("adc","a[^-b]c",[]))), + <<"">> = iolist_to_binary(join(re:split("adc","a[^]b]c",[trim]))), <<":">> = iolist_to_binary(join(re:split("adc","a[^]b]c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("adc","a[^]b]c",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^]b]c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("adc","a[^]b]c",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^]b]c",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^]b]c",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^]b]c",[]))), - <<"">> = iolist_to_binary(join(re:split("a-c","a[^]b]c",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^]b]c",[]))), + <<"">> = iolist_to_binary(join(re:split("a-c","a[^]b]c",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-c","a[^]b]c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-c","a[^]b]c",[]))), - <<"a]c">> = iolist_to_binary(join(re:split("a]c","a[^]b]c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-c","a[^]b]c",[]))), + <<"a]c">> = iolist_to_binary(join(re:split("a]c","a[^]b]c",[trim]))), <<"a]c">> = iolist_to_binary(join(re:split("a]c","a[^]b]c",[{parts, - 2}]))), - <<"a]c">> = iolist_to_binary(join(re:split("a]c","a[^]b]c",[]))), - <<":-">> = iolist_to_binary(join(re:split("a-","\\ba\\b",[trim]))), + 2}]))), + <<"a]c">> = iolist_to_binary(join(re:split("a]c","a[^]b]c",[]))), + <<":-">> = iolist_to_binary(join(re:split("a-","\\ba\\b",[trim]))), <<":-">> = iolist_to_binary(join(re:split("a-","\\ba\\b",[{parts, - 2}]))), - <<":-">> = iolist_to_binary(join(re:split("a-","\\ba\\b",[]))), - <<"-">> = iolist_to_binary(join(re:split("-a","\\ba\\b",[trim]))), + 2}]))), + <<":-">> = iolist_to_binary(join(re:split("a-","\\ba\\b",[]))), + <<"-">> = iolist_to_binary(join(re:split("-a","\\ba\\b",[trim]))), <<"-:">> = iolist_to_binary(join(re:split("-a","\\ba\\b",[{parts, - 2}]))), - <<"-:">> = iolist_to_binary(join(re:split("-a","\\ba\\b",[]))), - <<"-:-">> = iolist_to_binary(join(re:split("-a-","\\ba\\b",[trim]))), + 2}]))), + <<"-:">> = iolist_to_binary(join(re:split("-a","\\ba\\b",[]))), + <<"-:-">> = iolist_to_binary(join(re:split("-a-","\\ba\\b",[trim]))), <<"-:-">> = iolist_to_binary(join(re:split("-a-","\\ba\\b",[{parts, - 2}]))), - <<"-:-">> = iolist_to_binary(join(re:split("-a-","\\ba\\b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\by\\b",[trim]))), + 2}]))), + <<"-:-">> = iolist_to_binary(join(re:split("-a-","\\ba\\b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\by\\b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\by\\b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\by\\b",[]))), - <<"xy">> = iolist_to_binary(join(re:split("xy","\\by\\b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\by\\b",[]))), + <<"xy">> = iolist_to_binary(join(re:split("xy","\\by\\b",[trim]))), <<"xy">> = iolist_to_binary(join(re:split("xy","\\by\\b",[{parts, - 2}]))), - <<"xy">> = iolist_to_binary(join(re:split("xy","\\by\\b",[]))), - <<"yz">> = iolist_to_binary(join(re:split("yz","\\by\\b",[trim]))), + 2}]))), + <<"xy">> = iolist_to_binary(join(re:split("xy","\\by\\b",[]))), + <<"yz">> = iolist_to_binary(join(re:split("yz","\\by\\b",[trim]))), <<"yz">> = iolist_to_binary(join(re:split("yz","\\by\\b",[{parts, - 2}]))), - <<"yz">> = iolist_to_binary(join(re:split("yz","\\by\\b",[]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","\\by\\b",[trim]))), + 2}]))), + <<"yz">> = iolist_to_binary(join(re:split("yz","\\by\\b",[]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","\\by\\b",[trim]))), <<"xyz">> = iolist_to_binary(join(re:split("xyz","\\by\\b",[{parts, - 2}]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","\\by\\b",[]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","\\Ba\\B",[trim]))), + 2}]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","\\by\\b",[]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","\\Ba\\B",[trim]))), <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","\\Ba\\B",[{parts, - 2}]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","\\Ba\\B",[]))), - <<"a-">> = iolist_to_binary(join(re:split("a-","\\Ba\\B",[trim]))), + 2}]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","\\Ba\\B",[]))), + <<"a-">> = iolist_to_binary(join(re:split("a-","\\Ba\\B",[trim]))), <<"a-">> = iolist_to_binary(join(re:split("a-","\\Ba\\B",[{parts, - 2}]))), - <<"a-">> = iolist_to_binary(join(re:split("a-","\\Ba\\B",[]))), - <<"-a">> = iolist_to_binary(join(re:split("-a","\\Ba\\B",[trim]))), + 2}]))), + <<"a-">> = iolist_to_binary(join(re:split("a-","\\Ba\\B",[]))), + <<"-a">> = iolist_to_binary(join(re:split("-a","\\Ba\\B",[trim]))), <<"-a">> = iolist_to_binary(join(re:split("-a","\\Ba\\B",[{parts, - 2}]))), - <<"-a">> = iolist_to_binary(join(re:split("-a","\\Ba\\B",[]))), - <<"-a-">> = iolist_to_binary(join(re:split("-a-","\\Ba\\B",[trim]))), + 2}]))), + <<"-a">> = iolist_to_binary(join(re:split("-a","\\Ba\\B",[]))), + <<"-a-">> = iolist_to_binary(join(re:split("-a-","\\Ba\\B",[trim]))), <<"-a-">> = iolist_to_binary(join(re:split("-a-","\\Ba\\B",[{parts, - 2}]))), - <<"-a-">> = iolist_to_binary(join(re:split("-a-","\\Ba\\B",[]))), - <<"x">> = iolist_to_binary(join(re:split("xy","\\By\\b",[trim]))), + 2}]))), + <<"-a-">> = iolist_to_binary(join(re:split("-a-","\\Ba\\B",[]))), + <<"x">> = iolist_to_binary(join(re:split("xy","\\By\\b",[trim]))), <<"x:">> = iolist_to_binary(join(re:split("xy","\\By\\b",[{parts, - 2}]))), - <<"x:">> = iolist_to_binary(join(re:split("xy","\\By\\b",[]))), - <<":z">> = iolist_to_binary(join(re:split("yz","\\by\\B",[trim]))), + 2}]))), + <<"x:">> = iolist_to_binary(join(re:split("xy","\\By\\b",[]))), + <<":z">> = iolist_to_binary(join(re:split("yz","\\by\\B",[trim]))), <<":z">> = iolist_to_binary(join(re:split("yz","\\by\\B",[{parts, - 2}]))), - <<":z">> = iolist_to_binary(join(re:split("yz","\\by\\B",[]))), - <<"x:z">> = iolist_to_binary(join(re:split("xyz","\\By\\B",[trim]))), + 2}]))), + <<":z">> = iolist_to_binary(join(re:split("yz","\\by\\B",[]))), + <<"x:z">> = iolist_to_binary(join(re:split("xyz","\\By\\B",[trim]))), <<"x:z">> = iolist_to_binary(join(re:split("xyz","\\By\\B",[{parts, - 2}]))), - <<"x:z">> = iolist_to_binary(join(re:split("xyz","\\By\\B",[]))), - <<"">> = iolist_to_binary(join(re:split("a","\\w",[trim]))), + 2}]))), + <<"x:z">> = iolist_to_binary(join(re:split("xyz","\\By\\B",[]))), + <<"">> = iolist_to_binary(join(re:split("a","\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("-","\\W",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("-","\\W",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","\\W",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","\\W",[]))), - <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","\\W",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","\\W",[]))), + <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","\\W",[trim]))), <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\W",[{parts, - 2}]))), - <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","\\W",[]))), - <<"">> = iolist_to_binary(join(re:split("-","\\W",[trim]))), + 2}]))), + <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","\\W",[]))), + <<"">> = iolist_to_binary(join(re:split("-","\\W",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","\\W",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","\\W",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","\\W",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","\\W",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","\\W",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","\\W",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","\\W",[]))), - <<"">> = iolist_to_binary(join(re:split("a b","a\\sb",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","\\W",[]))), + <<"">> = iolist_to_binary(join(re:split("a b","a\\sb",[trim]))), <<":">> = iolist_to_binary(join(re:split("a b","a\\sb",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a b","a\\sb",[]))), - <<"">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a b","a\\sb",[]))), + <<"">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Sb",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Sb",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Sb",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Sb",[]))), - <<"">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Sb",[]))), + <<"">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[]))), - <<"a b">> = iolist_to_binary(join(re:split("a b","a\\Sb",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-b","a\\Sb",[]))), + <<"a b">> = iolist_to_binary(join(re:split("a b","a\\Sb",[trim]))), <<"a b">> = iolist_to_binary(join(re:split("a b","a\\Sb",[{parts, - 2}]))), - <<"a b">> = iolist_to_binary(join(re:split("a b","a\\Sb",[]))), - <<"">> = iolist_to_binary(join(re:split("1","\\d",[trim]))), + 2}]))), + <<"a b">> = iolist_to_binary(join(re:split("a b","a\\Sb",[]))), + <<"">> = iolist_to_binary(join(re:split("1","\\d",[trim]))), <<":">> = iolist_to_binary(join(re:split("1","\\d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1","\\d",[]))), - <<"">> = iolist_to_binary(join(re:split("-","\\D",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1","\\d",[]))), + <<"">> = iolist_to_binary(join(re:split("-","\\D",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","\\D",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","\\D",[]))), - <<"">> = iolist_to_binary(join(re:split("*** Failers","\\D",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","\\D",[]))), + <<"">> = iolist_to_binary(join(re:split("*** Failers","\\D",[trim]))), <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\D",[{parts, - 2}]))), - <<":::::::::::">> = iolist_to_binary(join(re:split("*** Failers","\\D",[]))), - <<"">> = iolist_to_binary(join(re:split("-","\\D",[trim]))), + 2}]))), + <<":::::::::::">> = iolist_to_binary(join(re:split("*** Failers","\\D",[]))), + <<"">> = iolist_to_binary(join(re:split("-","\\D",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","\\D",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","\\D",[]))), - <<"1">> = iolist_to_binary(join(re:split("1","\\D",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","\\D",[]))), + <<"1">> = iolist_to_binary(join(re:split("1","\\D",[trim]))), <<"1">> = iolist_to_binary(join(re:split("1","\\D",[{parts, - 2}]))), - <<"1">> = iolist_to_binary(join(re:split("1","\\D",[]))), + 2}]))), + <<"1">> = iolist_to_binary(join(re:split("1","\\D",[]))), ok. run17() -> - <<"">> = iolist_to_binary(join(re:split("a","[\\w]",[trim]))), + <<"">> = iolist_to_binary(join(re:split("a","[\\w]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","[\\w]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","[\\w]",[]))), - <<"">> = iolist_to_binary(join(re:split("-","[\\W]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","[\\w]",[]))), + <<"">> = iolist_to_binary(join(re:split("-","[\\W]",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","[\\W]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","[\\W]",[]))), - <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\W]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","[\\W]",[]))), + <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\W]",[trim]))), <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\W]",[{parts, - 2}]))), - <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\W]",[]))), - <<"">> = iolist_to_binary(join(re:split("-","[\\W]",[trim]))), + 2}]))), + <<"::::Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\W]",[]))), + <<"">> = iolist_to_binary(join(re:split("-","[\\W]",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","[\\W]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","[\\W]",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","[\\W]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","[\\W]",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","[\\W]",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","[\\W]",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","[\\W]",[]))), - <<"">> = iolist_to_binary(join(re:split("a b","a[\\s]b",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","[\\W]",[]))), + <<"">> = iolist_to_binary(join(re:split("a b","a[\\s]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a b","a[\\s]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a b","a[\\s]b",[]))), - <<"">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a b","a[\\s]b",[]))), + <<"">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[\\S]b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[\\S]b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[\\S]b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[\\S]b",[]))), - <<"">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[\\S]b",[]))), + <<"">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[]))), - <<"a b">> = iolist_to_binary(join(re:split("a b","a[\\S]b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a-b","a[\\S]b",[]))), + <<"a b">> = iolist_to_binary(join(re:split("a b","a[\\S]b",[trim]))), <<"a b">> = iolist_to_binary(join(re:split("a b","a[\\S]b",[{parts, - 2}]))), - <<"a b">> = iolist_to_binary(join(re:split("a b","a[\\S]b",[]))), - <<"">> = iolist_to_binary(join(re:split("1","[\\d]",[trim]))), + 2}]))), + <<"a b">> = iolist_to_binary(join(re:split("a b","a[\\S]b",[]))), + <<"">> = iolist_to_binary(join(re:split("1","[\\d]",[trim]))), <<":">> = iolist_to_binary(join(re:split("1","[\\d]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1","[\\d]",[]))), - <<"">> = iolist_to_binary(join(re:split("-","[\\D]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1","[\\d]",[]))), + <<"">> = iolist_to_binary(join(re:split("-","[\\D]",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","[\\D]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","[\\D]",[]))), - <<"">> = iolist_to_binary(join(re:split("*** Failers","[\\D]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","[\\D]",[]))), + <<"">> = iolist_to_binary(join(re:split("*** Failers","[\\D]",[trim]))), <<":** Failers">> = iolist_to_binary(join(re:split("*** Failers","[\\D]",[{parts, - 2}]))), - <<":::::::::::">> = iolist_to_binary(join(re:split("*** Failers","[\\D]",[]))), - <<"">> = iolist_to_binary(join(re:split("-","[\\D]",[trim]))), + 2}]))), + <<":::::::::::">> = iolist_to_binary(join(re:split("*** Failers","[\\D]",[]))), + <<"">> = iolist_to_binary(join(re:split("-","[\\D]",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","[\\D]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","[\\D]",[]))), - <<"1">> = iolist_to_binary(join(re:split("1","[\\D]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","[\\D]",[]))), + <<"1">> = iolist_to_binary(join(re:split("1","[\\D]",[trim]))), <<"1">> = iolist_to_binary(join(re:split("1","[\\D]",[{parts, - 2}]))), - <<"1">> = iolist_to_binary(join(re:split("1","[\\D]",[]))), - <<":c">> = iolist_to_binary(join(re:split("abc","ab|cd",[trim]))), + 2}]))), + <<"1">> = iolist_to_binary(join(re:split("1","[\\D]",[]))), + <<":c">> = iolist_to_binary(join(re:split("abc","ab|cd",[trim]))), <<":c">> = iolist_to_binary(join(re:split("abc","ab|cd",[{parts, - 2}]))), - <<":c">> = iolist_to_binary(join(re:split("abc","ab|cd",[]))), - <<"">> = iolist_to_binary(join(re:split("abcd","ab|cd",[trim]))), + 2}]))), + <<":c">> = iolist_to_binary(join(re:split("abc","ab|cd",[]))), + <<"">> = iolist_to_binary(join(re:split("abcd","ab|cd",[trim]))), <<":cd">> = iolist_to_binary(join(re:split("abcd","ab|cd",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abcd","ab|cd",[]))), - <<"d">> = iolist_to_binary(join(re:split("def","()ef",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abcd","ab|cd",[]))), + <<"d">> = iolist_to_binary(join(re:split("def","()ef",[trim]))), <<"d::">> = iolist_to_binary(join(re:split("def","()ef",[{parts, - 2}]))), - <<"d::">> = iolist_to_binary(join(re:split("def","()ef",[]))), - <<"">> = iolist_to_binary(join(re:split("a(b","a\\(b",[trim]))), + 2}]))), + <<"d::">> = iolist_to_binary(join(re:split("def","()ef",[]))), + <<"">> = iolist_to_binary(join(re:split("a(b","a\\(b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a(b","a\\(b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a(b","a\\(b",[]))), - <<"">> = iolist_to_binary(join(re:split("ab","a\\(*b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a(b","a\\(b",[]))), + <<"">> = iolist_to_binary(join(re:split("ab","a\\(*b",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab","a\\(*b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","a\\(*b",[]))), - <<"">> = iolist_to_binary(join(re:split("a((b","a\\(*b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","a\\(*b",[]))), + <<"">> = iolist_to_binary(join(re:split("a((b","a\\(*b",[trim]))), <<":">> = iolist_to_binary(join(re:split("a((b","a\\(*b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a((b","a\\(*b",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","a\\\\b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a((b","a\\(*b",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","a\\\\b",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","a\\\\b",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","a\\\\b",[]))), - <<":a:a:bc">> = iolist_to_binary(join(re:split("abc","((a))",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","a\\\\b",[]))), + <<":a:a:bc">> = iolist_to_binary(join(re:split("abc","((a))",[trim]))), <<":a:a:bc">> = iolist_to_binary(join(re:split("abc","((a))",[{parts, - 2}]))), - <<":a:a:bc">> = iolist_to_binary(join(re:split("abc","((a))",[]))), - <<":a:c">> = iolist_to_binary(join(re:split("abc","(a)b(c)",[trim]))), + 2}]))), + <<":a:a:bc">> = iolist_to_binary(join(re:split("abc","((a))",[]))), + <<":a:c">> = iolist_to_binary(join(re:split("abc","(a)b(c)",[trim]))), <<":a:c:">> = iolist_to_binary(join(re:split("abc","(a)b(c)",[{parts, - 2}]))), - <<":a:c:">> = iolist_to_binary(join(re:split("abc","(a)b(c)",[]))), - <<"aabb">> = iolist_to_binary(join(re:split("aabbabc","a+b+c",[trim]))), + 2}]))), + <<":a:c:">> = iolist_to_binary(join(re:split("abc","(a)b(c)",[]))), + <<"aabb">> = iolist_to_binary(join(re:split("aabbabc","a+b+c",[trim]))), <<"aabb:">> = iolist_to_binary(join(re:split("aabbabc","a+b+c",[{parts, - 2}]))), - <<"aabb:">> = iolist_to_binary(join(re:split("aabbabc","a+b+c",[]))), - <<"aabb">> = iolist_to_binary(join(re:split("aabbabc","a{1,}b{1,}c",[trim]))), + 2}]))), + <<"aabb:">> = iolist_to_binary(join(re:split("aabbabc","a+b+c",[]))), + <<"aabb">> = iolist_to_binary(join(re:split("aabbabc","a{1,}b{1,}c",[trim]))), <<"aabb:">> = iolist_to_binary(join(re:split("aabbabc","a{1,}b{1,}c",[{parts, - 2}]))), - <<"aabb:">> = iolist_to_binary(join(re:split("aabbabc","a{1,}b{1,}c",[]))), - <<"">> = iolist_to_binary(join(re:split("abcabc","a.+?c",[trim]))), + 2}]))), + <<"aabb:">> = iolist_to_binary(join(re:split("aabbabc","a{1,}b{1,}c",[]))), + <<"">> = iolist_to_binary(join(re:split("abcabc","a.+?c",[trim]))), <<":abc">> = iolist_to_binary(join(re:split("abcabc","a.+?c",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abcabc","a.+?c",[]))), - <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b)*",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abcabc","a.+?c",[]))), + <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b)*",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b)*",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b)*",[]))), - <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b){0,}",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b)*",[]))), + <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b){0,}",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b){0,}",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b){0,}",[]))), - <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b)+",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b){0,}",[]))), + <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b)+",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b)+",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b)+",[]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b)+",[]))), ok. run18() -> - <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b){1,}",[trim]))), + <<":b">> = iolist_to_binary(join(re:split("ab","(a+|b){1,}",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b){1,}",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b){1,}",[]))), - <<":a::b">> = iolist_to_binary(join(re:split("ab","(a+|b)?",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("ab","(a+|b){1,}",[]))), + <<":a::b">> = iolist_to_binary(join(re:split("ab","(a+|b)?",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("ab","(a+|b)?",[{parts, - 2}]))), - <<":a::b:">> = iolist_to_binary(join(re:split("ab","(a+|b)?",[]))), - <<":a::b">> = iolist_to_binary(join(re:split("ab","(a+|b){0,1}",[trim]))), + 2}]))), + <<":a::b:">> = iolist_to_binary(join(re:split("ab","(a+|b)?",[]))), + <<":a::b">> = iolist_to_binary(join(re:split("ab","(a+|b){0,1}",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("ab","(a+|b){0,1}",[{parts, - 2}]))), - <<":a::b:">> = iolist_to_binary(join(re:split("ab","(a+|b){0,1}",[]))), - <<"">> = iolist_to_binary(join(re:split("cde","[^ab]*",[trim]))), + 2}]))), + <<":a::b:">> = iolist_to_binary(join(re:split("ab","(a+|b){0,1}",[]))), + <<"">> = iolist_to_binary(join(re:split("cde","[^ab]*",[trim]))), <<":">> = iolist_to_binary(join(re:split("cde","[^ab]*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("cde","[^ab]*",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("cde","[^ab]*",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[]))), - <<"b">> = iolist_to_binary(join(re:split("b","abc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[]))), + <<"b">> = iolist_to_binary(join(re:split("b","abc",[trim]))), <<"b">> = iolist_to_binary(join(re:split("b","abc",[{parts, - 2}]))), - <<"b">> = iolist_to_binary(join(re:split("b","abc",[]))), - <<":c">> = iolist_to_binary(join(re:split("abbbcd","([abc])*d",[trim]))), + 2}]))), + <<"b">> = iolist_to_binary(join(re:split("b","abc",[]))), + <<":c">> = iolist_to_binary(join(re:split("abbbcd","([abc])*d",[trim]))), <<":c:">> = iolist_to_binary(join(re:split("abbbcd","([abc])*d",[{parts, - 2}]))), - <<":c:">> = iolist_to_binary(join(re:split("abbbcd","([abc])*d",[]))), - <<":a">> = iolist_to_binary(join(re:split("abcd","([abc])*bcd",[trim]))), + 2}]))), + <<":c:">> = iolist_to_binary(join(re:split("abbbcd","([abc])*d",[]))), + <<":a">> = iolist_to_binary(join(re:split("abcd","([abc])*bcd",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("abcd","([abc])*bcd",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("abcd","([abc])*bcd",[]))), - <<"">> = iolist_to_binary(join(re:split("e","a|b|c|d|e",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("abcd","([abc])*bcd",[]))), + <<"">> = iolist_to_binary(join(re:split("e","a|b|c|d|e",[trim]))), <<":">> = iolist_to_binary(join(re:split("e","a|b|c|d|e",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("e","a|b|c|d|e",[]))), - <<":e">> = iolist_to_binary(join(re:split("ef","(a|b|c|d|e)f",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("e","a|b|c|d|e",[]))), + <<":e">> = iolist_to_binary(join(re:split("ef","(a|b|c|d|e)f",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("ef","(a|b|c|d|e)f",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("ef","(a|b|c|d|e)f",[]))), - <<"">> = iolist_to_binary(join(re:split("abcdefg","abcd*efg",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("ef","(a|b|c|d|e)f",[]))), + <<"">> = iolist_to_binary(join(re:split("abcdefg","abcd*efg",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcdefg","abcd*efg",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcdefg","abcd*efg",[]))), - <<"x:y:z">> = iolist_to_binary(join(re:split("xabyabbbz","ab*",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcdefg","abcd*efg",[]))), + <<"x:y:z">> = iolist_to_binary(join(re:split("xabyabbbz","ab*",[trim]))), <<"x:yabbbz">> = iolist_to_binary(join(re:split("xabyabbbz","ab*",[{parts, - 2}]))), - <<"x:y:z">> = iolist_to_binary(join(re:split("xabyabbbz","ab*",[]))), - <<"x:y:z">> = iolist_to_binary(join(re:split("xayabbbz","ab*",[trim]))), + 2}]))), + <<"x:y:z">> = iolist_to_binary(join(re:split("xabyabbbz","ab*",[]))), + <<"x:y:z">> = iolist_to_binary(join(re:split("xayabbbz","ab*",[trim]))), <<"x:yabbbz">> = iolist_to_binary(join(re:split("xayabbbz","ab*",[{parts, - 2}]))), - <<"x:y:z">> = iolist_to_binary(join(re:split("xayabbbz","ab*",[]))), - <<"ab:cd">> = iolist_to_binary(join(re:split("abcde","(ab|cd)e",[trim]))), + 2}]))), + <<"x:y:z">> = iolist_to_binary(join(re:split("xayabbbz","ab*",[]))), + <<"ab:cd">> = iolist_to_binary(join(re:split("abcde","(ab|cd)e",[trim]))), <<"ab:cd:">> = iolist_to_binary(join(re:split("abcde","(ab|cd)e",[{parts, - 2}]))), - <<"ab:cd:">> = iolist_to_binary(join(re:split("abcde","(ab|cd)e",[]))), - <<"">> = iolist_to_binary(join(re:split("hij","[abhgefdc]ij",[trim]))), + 2}]))), + <<"ab:cd:">> = iolist_to_binary(join(re:split("abcde","(ab|cd)e",[]))), + <<"">> = iolist_to_binary(join(re:split("hij","[abhgefdc]ij",[trim]))), <<":">> = iolist_to_binary(join(re:split("hij","[abhgefdc]ij",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("hij","[abhgefdc]ij",[]))), - <<"abcd">> = iolist_to_binary(join(re:split("abcdef","(abc|)ef",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("hij","[abhgefdc]ij",[]))), + <<"abcd">> = iolist_to_binary(join(re:split("abcdef","(abc|)ef",[trim]))), <<"abcd::">> = iolist_to_binary(join(re:split("abcdef","(abc|)ef",[{parts, - 2}]))), - <<"abcd::">> = iolist_to_binary(join(re:split("abcdef","(abc|)ef",[]))), - <<"a:b">> = iolist_to_binary(join(re:split("abcd","(a|b)c*d",[trim]))), + 2}]))), + <<"abcd::">> = iolist_to_binary(join(re:split("abcdef","(abc|)ef",[]))), + <<"a:b">> = iolist_to_binary(join(re:split("abcd","(a|b)c*d",[trim]))), <<"a:b:">> = iolist_to_binary(join(re:split("abcd","(a|b)c*d",[{parts, - 2}]))), - <<"a:b:">> = iolist_to_binary(join(re:split("abcd","(a|b)c*d",[]))), - <<":a">> = iolist_to_binary(join(re:split("abc","(ab|ab*)bc",[trim]))), + 2}]))), + <<"a:b:">> = iolist_to_binary(join(re:split("abcd","(a|b)c*d",[]))), + <<":a">> = iolist_to_binary(join(re:split("abc","(ab|ab*)bc",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("abc","(ab|ab*)bc",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("abc","(ab|ab*)bc",[]))), - <<":bc">> = iolist_to_binary(join(re:split("abc","a([bc]*)c*",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("abc","(ab|ab*)bc",[]))), + <<":bc">> = iolist_to_binary(join(re:split("abc","a([bc]*)c*",[trim]))), <<":bc:">> = iolist_to_binary(join(re:split("abc","a([bc]*)c*",[{parts, - 2}]))), - <<":bc:">> = iolist_to_binary(join(re:split("abc","a([bc]*)c*",[]))), - <<":bc:d">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c*d)",[trim]))), + 2}]))), + <<":bc:">> = iolist_to_binary(join(re:split("abc","a([bc]*)c*",[]))), + <<":bc:d">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c*d)",[trim]))), <<":bc:d:">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c*d)",[{parts, - 2}]))), - <<":bc:d:">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c*d)",[]))), + 2}]))), + <<":bc:d:">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c*d)",[]))), ok. run19() -> - <<":bc:d">> = iolist_to_binary(join(re:split("abcd","a([bc]+)(c*d)",[trim]))), + <<":bc:d">> = iolist_to_binary(join(re:split("abcd","a([bc]+)(c*d)",[trim]))), <<":bc:d:">> = iolist_to_binary(join(re:split("abcd","a([bc]+)(c*d)",[{parts, - 2}]))), - <<":bc:d:">> = iolist_to_binary(join(re:split("abcd","a([bc]+)(c*d)",[]))), - <<":b:cd">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c+d)",[trim]))), + 2}]))), + <<":bc:d:">> = iolist_to_binary(join(re:split("abcd","a([bc]+)(c*d)",[]))), + <<":b:cd">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c+d)",[trim]))), <<":b:cd:">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c+d)",[{parts, - 2}]))), - <<":b:cd:">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c+d)",[]))), - <<"">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]*dcdcde",[trim]))), + 2}]))), + <<":b:cd:">> = iolist_to_binary(join(re:split("abcd","a([bc]*)(c+d)",[]))), + <<"">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]*dcdcde",[trim]))), <<":">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]*dcdcde",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]*dcdcde",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bcd]+dcdcde",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]*dcdcde",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bcd]+dcdcde",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bcd]+dcdcde",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bcd]+dcdcde",[]))), - <<"abcde">> = iolist_to_binary(join(re:split("abcde","a[bcd]+dcdcde",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[bcd]+dcdcde",[]))), + <<"abcde">> = iolist_to_binary(join(re:split("abcde","a[bcd]+dcdcde",[trim]))), <<"abcde">> = iolist_to_binary(join(re:split("abcde","a[bcd]+dcdcde",[{parts, - 2}]))), - <<"abcde">> = iolist_to_binary(join(re:split("abcde","a[bcd]+dcdcde",[]))), - <<"adcdcde">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]+dcdcde",[trim]))), + 2}]))), + <<"abcde">> = iolist_to_binary(join(re:split("abcde","a[bcd]+dcdcde",[]))), + <<"adcdcde">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]+dcdcde",[trim]))), <<"adcdcde">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]+dcdcde",[{parts, - 2}]))), - <<"adcdcde">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]+dcdcde",[]))), - <<":ab">> = iolist_to_binary(join(re:split("abc","(ab|a)b*c",[trim]))), + 2}]))), + <<"adcdcde">> = iolist_to_binary(join(re:split("adcdcde","a[bcd]+dcdcde",[]))), + <<":ab">> = iolist_to_binary(join(re:split("abc","(ab|a)b*c",[trim]))), <<":ab:">> = iolist_to_binary(join(re:split("abc","(ab|a)b*c",[{parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("abc","(ab|a)b*c",[]))), - <<":abc:a:b:d">> = iolist_to_binary(join(re:split("abcd","((a)(b)c)(d)",[trim]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("abc","(ab|a)b*c",[]))), + <<":abc:a:b:d">> = iolist_to_binary(join(re:split("abcd","((a)(b)c)(d)",[trim]))), <<":abc:a:b:d:">> = iolist_to_binary(join(re:split("abcd","((a)(b)c)(d)",[{parts, - 2}]))), - <<":abc:a:b:d:">> = iolist_to_binary(join(re:split("abcd","((a)(b)c)(d)",[]))), - <<"">> = iolist_to_binary(join(re:split("alpha","[a-zA-Z_][a-zA-Z0-9_]*",[trim]))), + 2}]))), + <<":abc:a:b:d:">> = iolist_to_binary(join(re:split("abcd","((a)(b)c)(d)",[]))), + <<"">> = iolist_to_binary(join(re:split("alpha","[a-zA-Z_][a-zA-Z0-9_]*",[trim]))), <<":">> = iolist_to_binary(join(re:split("alpha","[a-zA-Z_][a-zA-Z0-9_]*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("alpha","[a-zA-Z_][a-zA-Z0-9_]*",[]))), - <<"a">> = iolist_to_binary(join(re:split("abh","^a(bc+|b[eh])g|.h$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("alpha","[a-zA-Z_][a-zA-Z0-9_]*",[]))), + <<"a">> = iolist_to_binary(join(re:split("abh","^a(bc+|b[eh])g|.h$",[trim]))), <<"a::">> = iolist_to_binary(join(re:split("abh","^a(bc+|b[eh])g|.h$",[{parts, - 2}]))), - <<"a::">> = iolist_to_binary(join(re:split("abh","^a(bc+|b[eh])g|.h$",[]))), - <<":effgz">> = iolist_to_binary(join(re:split("effgz","(bc+d$|ef*g.|h?i(j|k))",[trim]))), + 2}]))), + <<"a::">> = iolist_to_binary(join(re:split("abh","^a(bc+|b[eh])g|.h$",[]))), + <<":effgz">> = iolist_to_binary(join(re:split("effgz","(bc+d$|ef*g.|h?i(j|k))",[trim]))), <<":effgz::">> = iolist_to_binary(join(re:split("effgz","(bc+d$|ef*g.|h?i(j|k))",[{parts, - 2}]))), - <<":effgz::">> = iolist_to_binary(join(re:split("effgz","(bc+d$|ef*g.|h?i(j|k))",[]))), - <<":ij:j">> = iolist_to_binary(join(re:split("ij","(bc+d$|ef*g.|h?i(j|k))",[trim]))), + 2}]))), + <<":effgz::">> = iolist_to_binary(join(re:split("effgz","(bc+d$|ef*g.|h?i(j|k))",[]))), + <<":ij:j">> = iolist_to_binary(join(re:split("ij","(bc+d$|ef*g.|h?i(j|k))",[trim]))), <<":ij:j:">> = iolist_to_binary(join(re:split("ij","(bc+d$|ef*g.|h?i(j|k))",[{parts, - 2}]))), - <<":ij:j:">> = iolist_to_binary(join(re:split("ij","(bc+d$|ef*g.|h?i(j|k))",[]))), - <<"r:effgz">> = iolist_to_binary(join(re:split("reffgz","(bc+d$|ef*g.|h?i(j|k))",[trim]))), + 2}]))), + <<":ij:j:">> = iolist_to_binary(join(re:split("ij","(bc+d$|ef*g.|h?i(j|k))",[]))), + <<"r:effgz">> = iolist_to_binary(join(re:split("reffgz","(bc+d$|ef*g.|h?i(j|k))",[trim]))), <<"r:effgz::">> = iolist_to_binary(join(re:split("reffgz","(bc+d$|ef*g.|h?i(j|k))",[{parts, - 2}]))), - <<"r:effgz::">> = iolist_to_binary(join(re:split("reffgz","(bc+d$|ef*g.|h?i(j|k))",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[trim]))), + 2}]))), + <<"r:effgz::">> = iolist_to_binary(join(re:split("reffgz","(bc+d$|ef*g.|h?i(j|k))",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[]))), - <<"effg">> = iolist_to_binary(join(re:split("effg","(bc+d$|ef*g.|h?i(j|k))",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[]))), + <<"effg">> = iolist_to_binary(join(re:split("effg","(bc+d$|ef*g.|h?i(j|k))",[trim]))), <<"effg">> = iolist_to_binary(join(re:split("effg","(bc+d$|ef*g.|h?i(j|k))",[{parts, - 2}]))), - <<"effg">> = iolist_to_binary(join(re:split("effg","(bc+d$|ef*g.|h?i(j|k))",[]))), - <<"bcdd">> = iolist_to_binary(join(re:split("bcdd","(bc+d$|ef*g.|h?i(j|k))",[trim]))), + 2}]))), + <<"effg">> = iolist_to_binary(join(re:split("effg","(bc+d$|ef*g.|h?i(j|k))",[]))), + <<"bcdd">> = iolist_to_binary(join(re:split("bcdd","(bc+d$|ef*g.|h?i(j|k))",[trim]))), <<"bcdd">> = iolist_to_binary(join(re:split("bcdd","(bc+d$|ef*g.|h?i(j|k))",[{parts, - 2}]))), - <<"bcdd">> = iolist_to_binary(join(re:split("bcdd","(bc+d$|ef*g.|h?i(j|k))",[]))), - <<":a:a:a:a:a:a:a:a:a:a">> = iolist_to_binary(join(re:split("a","((((((((((a))))))))))",[trim]))), + 2}]))), + <<"bcdd">> = iolist_to_binary(join(re:split("bcdd","(bc+d$|ef*g.|h?i(j|k))",[]))), + <<":a:a:a:a:a:a:a:a:a:a">> = iolist_to_binary(join(re:split("a","((((((((((a))))))))))",[trim]))), <<":a:a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("a","((((((((((a))))))))))",[{parts, - 2}]))), - <<":a:a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("a","((((((((((a))))))))))",[]))), - <<":a:a:a:a:a:a:a:a:a:a">> = iolist_to_binary(join(re:split("aa","((((((((((a))))))))))\\10",[trim]))), + 2}]))), + <<":a:a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("a","((((((((((a))))))))))",[]))), + <<":a:a:a:a:a:a:a:a:a:a">> = iolist_to_binary(join(re:split("aa","((((((((((a))))))))))\\10",[trim]))), <<":a:a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("aa","((((((((((a))))))))))\\10",[{parts, - 2}]))), - <<":a:a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("aa","((((((((((a))))))))))\\10",[]))), - <<":a:a:a:a:a:a:a:a:a">> = iolist_to_binary(join(re:split("a","(((((((((a)))))))))",[trim]))), + 2}]))), + <<":a:a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("aa","((((((((((a))))))))))\\10",[]))), + <<":a:a:a:a:a:a:a:a:a">> = iolist_to_binary(join(re:split("a","(((((((((a)))))))))",[trim]))), <<":a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("a","(((((((((a)))))))))",[{parts, - 2}]))), - <<":a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("a","(((((((((a)))))))))",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[trim]))), + 2}]))), + <<":a:a:a:a:a:a:a:a:a:">> = iolist_to_binary(join(re:split("a","(((((((((a)))))))))",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","multiple words of text",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","multiple words of text",[trim]))), <<"aa">> = iolist_to_binary(join(re:split("aa","multiple words of text",[{parts, - 2}]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","multiple words of text",[]))), - <<"uh-uh">> = iolist_to_binary(join(re:split("uh-uh","multiple words of text",[trim]))), + 2}]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","multiple words of text",[]))), + <<"uh-uh">> = iolist_to_binary(join(re:split("uh-uh","multiple words of text",[trim]))), <<"uh-uh">> = iolist_to_binary(join(re:split("uh-uh","multiple words of text",[{parts, - 2}]))), - <<"uh-uh">> = iolist_to_binary(join(re:split("uh-uh","multiple words of text",[]))), - <<":, yeah">> = iolist_to_binary(join(re:split("multiple words, yeah","multiple words",[trim]))), + 2}]))), + <<"uh-uh">> = iolist_to_binary(join(re:split("uh-uh","multiple words of text",[]))), + <<":, yeah">> = iolist_to_binary(join(re:split("multiple words, yeah","multiple words",[trim]))), <<":, yeah">> = iolist_to_binary(join(re:split("multiple words, yeah","multiple words",[{parts, - 2}]))), - <<":, yeah">> = iolist_to_binary(join(re:split("multiple words, yeah","multiple words",[]))), - <<":ab:de">> = iolist_to_binary(join(re:split("abcde","(.*)c(.*)",[trim]))), + 2}]))), + <<":, yeah">> = iolist_to_binary(join(re:split("multiple words, yeah","multiple words",[]))), + <<":ab:de">> = iolist_to_binary(join(re:split("abcde","(.*)c(.*)",[trim]))), <<":ab:de:">> = iolist_to_binary(join(re:split("abcde","(.*)c(.*)",[{parts, - 2}]))), - <<":ab:de:">> = iolist_to_binary(join(re:split("abcde","(.*)c(.*)",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("(a, b)","\\((.*), (.*)\\)",[trim]))), + 2}]))), + <<":ab:de:">> = iolist_to_binary(join(re:split("abcde","(.*)c(.*)",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("(a, b)","\\((.*), (.*)\\)",[trim]))), <<":a:b:">> = iolist_to_binary(join(re:split("(a, b)","\\((.*), (.*)\\)",[{parts, - 2}]))), - <<":a:b:">> = iolist_to_binary(join(re:split("(a, b)","\\((.*), (.*)\\)",[]))), - <<"">> = iolist_to_binary(join(re:split("abcd","abcd",[trim]))), + 2}]))), + <<":a:b:">> = iolist_to_binary(join(re:split("(a, b)","\\((.*), (.*)\\)",[]))), + <<"">> = iolist_to_binary(join(re:split("abcd","abcd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcd","abcd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcd","abcd",[]))), - <<":bc">> = iolist_to_binary(join(re:split("abcd","a(bc)d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcd","abcd",[]))), + <<":bc">> = iolist_to_binary(join(re:split("abcd","a(bc)d",[trim]))), <<":bc:">> = iolist_to_binary(join(re:split("abcd","a(bc)d",[{parts, - 2}]))), - <<":bc:">> = iolist_to_binary(join(re:split("abcd","a(bc)d",[]))), - <<"">> = iolist_to_binary(join(re:split("ac","a[-]?c",[trim]))), + 2}]))), + <<":bc:">> = iolist_to_binary(join(re:split("abcd","a(bc)d",[]))), + <<"">> = iolist_to_binary(join(re:split("ac","a[-]?c",[trim]))), <<":">> = iolist_to_binary(join(re:split("ac","a[-]?c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ac","a[-]?c",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ac","a[-]?c",[]))), ok. run20() -> - <<":abc">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[trim]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcabc","([a-c]*)\\1",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(abc)\\1",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabc","([a-c]*)\\1",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","([a-c]*)\\1",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","([a-c]*)\\1",[]))), - <<":a">> = iolist_to_binary(join(re:split("a","(a)|\\1",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","([a-c]*)\\1",[]))), + <<":a">> = iolist_to_binary(join(re:split("a","(a)|\\1",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("a","(a)|\\1",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("a","(a)|\\1",[]))), - <<"*** F:a:ilers">> = iolist_to_binary(join(re:split("*** Failers","(a)|\\1",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("a","(a)|\\1",[]))), + <<"*** F:a:ilers">> = iolist_to_binary(join(re:split("*** Failers","(a)|\\1",[trim]))), <<"*** F:a:ilers">> = iolist_to_binary(join(re:split("*** Failers","(a)|\\1",[{parts, - 2}]))), - <<"*** F:a:ilers">> = iolist_to_binary(join(re:split("*** Failers","(a)|\\1",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("ab","(a)|\\1",[trim]))), + 2}]))), + <<"*** F:a:ilers">> = iolist_to_binary(join(re:split("*** Failers","(a)|\\1",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("ab","(a)|\\1",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("ab","(a)|\\1",[{parts, - 2}]))), - <<":a:b">> = iolist_to_binary(join(re:split("ab","(a)|\\1",[]))), - <<"x">> = iolist_to_binary(join(re:split("x","(a)|\\1",[trim]))), + 2}]))), + <<":a:b">> = iolist_to_binary(join(re:split("ab","(a)|\\1",[]))), + <<"x">> = iolist_to_binary(join(re:split("x","(a)|\\1",[trim]))), <<"x">> = iolist_to_binary(join(re:split("x","(a)|\\1",[{parts, - 2}]))), - <<"x">> = iolist_to_binary(join(re:split("x","(a)|\\1",[]))), - <<":bb:b:b:cbc:c">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2)*",[trim]))), + 2}]))), + <<"x">> = iolist_to_binary(join(re:split("x","(a)|\\1",[]))), + <<":bb:b:b:cbc:c">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2)*",[trim]))), <<":bb:b:bcbc">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2)*",[{parts, - 2}]))), - <<":bb:b:b:cbc:c:">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2)*",[]))), - <<":cbc:c">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2){3}",[trim]))), + 2}]))), + <<":bb:b:b:cbc:c:">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2)*",[]))), + <<":cbc:c">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2){3}",[trim]))), <<":cbc:c:">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2){3}",[{parts, - 2}]))), - <<":cbc:c:">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2){3}",[]))), - <<"aaaxabaxbaax:bbax:b:a">> = iolist_to_binary(join(re:split("aaaxabaxbaaxbbax","((\\3|b)\\2(a)x)+",[trim]))), + 2}]))), + <<":cbc:c:">> = iolist_to_binary(join(re:split("ababbbcbc","(([a-c])b*?\\2){3}",[]))), + <<"aaaxabaxbaax:bbax:b:a">> = iolist_to_binary(join(re:split("aaaxabaxbaaxbbax","((\\3|b)\\2(a)x)+",[trim]))), <<"aaaxabaxbaax:bbax:b:a:">> = iolist_to_binary(join(re:split("aaaxabaxbaaxbbax","((\\3|b)\\2(a)x)+",[{parts, - 2}]))), - <<"aaaxabaxbaax:bbax:b:a:">> = iolist_to_binary(join(re:split("aaaxabaxbaaxbbax","((\\3|b)\\2(a)x)+",[]))), - <<"bbaababbabaaaaa:bba:b:a">> = iolist_to_binary(join(re:split("bbaababbabaaaaabbaaaabba","((\\3|b)\\2(a)){2,}",[trim]))), + 2}]))), + <<"aaaxabaxbaax:bbax:b:a:">> = iolist_to_binary(join(re:split("aaaxabaxbaaxbbax","((\\3|b)\\2(a)x)+",[]))), + <<"bbaababbabaaaaa:bba:b:a">> = iolist_to_binary(join(re:split("bbaababbabaaaaabbaaaabba","((\\3|b)\\2(a)){2,}",[trim]))), <<"bbaababbabaaaaa:bba:b:a:">> = iolist_to_binary(join(re:split("bbaababbabaaaaabbaaaabba","((\\3|b)\\2(a)){2,}",[{parts, - 2}]))), - <<"bbaababbabaaaaa:bba:b:a:">> = iolist_to_binary(join(re:split("bbaababbabaaaaabbaaaabba","((\\3|b)\\2(a)){2,}",[]))), + 2}]))), + <<"bbaababbabaaaaa:bba:b:a:">> = iolist_to_binary(join(re:split("bbaababbabaaaaabbaaaabba","((\\3|b)\\2(a)){2,}",[]))), <<"">> = iolist_to_binary(join(re:split("ABC","abc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","abc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","abc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","abc",[caseless]))), <<"X:Y">> = iolist_to_binary(join(re:split("XABCY","abc",[caseless, - trim]))), + trim]))), <<"X:Y">> = iolist_to_binary(join(re:split("XABCY","abc",[caseless, {parts, - 2}]))), - <<"X:Y">> = iolist_to_binary(join(re:split("XABCY","abc",[caseless]))), + 2}]))), + <<"X:Y">> = iolist_to_binary(join(re:split("XABCY","abc",[caseless]))), <<"AB">> = iolist_to_binary(join(re:split("ABABC","abc",[caseless, - trim]))), + trim]))), <<"AB:">> = iolist_to_binary(join(re:split("ABABC","abc",[caseless, {parts, - 2}]))), - <<"AB:">> = iolist_to_binary(join(re:split("ABABC","abc",[caseless]))), + 2}]))), + <<"AB:">> = iolist_to_binary(join(re:split("ABABC","abc",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","abc",[caseless]))), <<"aaxabxbaxbbx">> = iolist_to_binary(join(re:split("aaxabxbaxbbx","abc",[caseless, - trim]))), + trim]))), <<"aaxabxbaxbbx">> = iolist_to_binary(join(re:split("aaxabxbaxbbx","abc",[caseless, {parts, - 2}]))), - <<"aaxabxbaxbbx">> = iolist_to_binary(join(re:split("aaxabxbaxbbx","abc",[caseless]))), + 2}]))), + <<"aaxabxbaxbbx">> = iolist_to_binary(join(re:split("aaxabxbaxbbx","abc",[caseless]))), <<"XBC">> = iolist_to_binary(join(re:split("XBC","abc",[caseless, - trim]))), + trim]))), <<"XBC">> = iolist_to_binary(join(re:split("XBC","abc",[caseless, {parts, - 2}]))), - <<"XBC">> = iolist_to_binary(join(re:split("XBC","abc",[caseless]))), + 2}]))), + <<"XBC">> = iolist_to_binary(join(re:split("XBC","abc",[caseless]))), <<"AXC">> = iolist_to_binary(join(re:split("AXC","abc",[caseless, - trim]))), + trim]))), <<"AXC">> = iolist_to_binary(join(re:split("AXC","abc",[caseless, {parts, - 2}]))), - <<"AXC">> = iolist_to_binary(join(re:split("AXC","abc",[caseless]))), + 2}]))), + <<"AXC">> = iolist_to_binary(join(re:split("AXC","abc",[caseless]))), <<"ABX">> = iolist_to_binary(join(re:split("ABX","abc",[caseless, - trim]))), + trim]))), <<"ABX">> = iolist_to_binary(join(re:split("ABX","abc",[caseless, {parts, - 2}]))), - <<"ABX">> = iolist_to_binary(join(re:split("ABX","abc",[caseless]))), + 2}]))), + <<"ABX">> = iolist_to_binary(join(re:split("ABX","abc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","ab*c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","ab*c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","ab*c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","ab*c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","ab*bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","ab*bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","ab*bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","ab*bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBC","ab*bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBC","ab*bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBC","ab*bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBC","ab*bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBBBC","ab*?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab*?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab*?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab*?bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBBBC","ab{0,}?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{0,}?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{0,}?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{0,}?bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBC","ab+?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBC","ab+?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBC","ab+?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBC","ab+?bc",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab+bc",[caseless]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","ab+bc",[caseless, - trim]))), + trim]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","ab+bc",[caseless, {parts, - 2}]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","ab+bc",[caseless]))), + 2}]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","ab+bc",[caseless]))), <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab+bc",[caseless, - trim]))), + trim]))), <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab+bc",[caseless, {parts, - 2}]))), - <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab+bc",[caseless]))), + 2}]))), + <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab+bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBBBC","ab+bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab+bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab+bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab+bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,}?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,}?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,}?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,}?bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,3}?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,3}?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,3}?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{1,3}?bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABBBBC","ab{3,4}?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{3,4}?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{3,4}?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBBBC","ab{3,4}?bc",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}?bc",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}?bc",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}?bc",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","ab{4,5}?bc",[caseless]))), <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab{4,5}?bc",[caseless, - trim]))), + trim]))), <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab{4,5}?bc",[caseless, {parts, - 2}]))), - <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab{4,5}?bc",[caseless]))), + 2}]))), + <<"ABQ">> = iolist_to_binary(join(re:split("ABQ","ab{4,5}?bc",[caseless]))), <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","ab{4,5}?bc",[caseless, - trim]))), + trim]))), <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","ab{4,5}?bc",[caseless, {parts, - 2}]))), - <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","ab{4,5}?bc",[caseless]))), + 2}]))), + <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","ab{4,5}?bc",[caseless]))), ok. run21() -> <<"">> = iolist_to_binary(join(re:split("ABBC","ab??bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABBC","ab??bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABBC","ab??bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABBC","ab??bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","ab??bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","ab??bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","ab??bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","ab??bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?bc",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?bc",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?bc",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?bc",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","ab??c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","ab??c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","ab??c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","ab??c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","ab{0,1}?c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","^abc$",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","^abc$",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","^abc$",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","^abc$",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^abc$",[caseless]))), <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","^abc$",[caseless, - trim]))), + trim]))), <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","^abc$",[caseless, {parts, - 2}]))), - <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","^abc$",[caseless]))), + 2}]))), + <<"ABBBBC">> = iolist_to_binary(join(re:split("ABBBBC","^abc$",[caseless]))), <<"ABCC">> = iolist_to_binary(join(re:split("ABCC","^abc$",[caseless, - trim]))), + trim]))), <<"ABCC">> = iolist_to_binary(join(re:split("ABCC","^abc$",[caseless, {parts, - 2}]))), - <<"ABCC">> = iolist_to_binary(join(re:split("ABCC","^abc$",[caseless]))), + 2}]))), + <<"ABCC">> = iolist_to_binary(join(re:split("ABCC","^abc$",[caseless]))), <<":C">> = iolist_to_binary(join(re:split("ABCC","^abc",[caseless, - trim]))), + trim]))), <<":C">> = iolist_to_binary(join(re:split("ABCC","^abc",[caseless, {parts, - 2}]))), - <<":C">> = iolist_to_binary(join(re:split("ABCC","^abc",[caseless]))), + 2}]))), + <<":C">> = iolist_to_binary(join(re:split("ABCC","^abc",[caseless]))), <<"A">> = iolist_to_binary(join(re:split("AABC","abc$",[caseless, - trim]))), + trim]))), <<"A:">> = iolist_to_binary(join(re:split("AABC","abc$",[caseless, {parts, - 2}]))), - <<"A:">> = iolist_to_binary(join(re:split("AABC","abc$",[caseless]))), + 2}]))), + <<"A:">> = iolist_to_binary(join(re:split("AABC","abc$",[caseless]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","^",[caseless, - trim]))), + trim]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","^",[caseless, {parts, - 2}]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","^",[caseless]))), + 2}]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","^",[caseless]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","$",[caseless, - trim]))), + trim]))), <<"ABC:">> = iolist_to_binary(join(re:split("ABC","$",[caseless, {parts, - 2}]))), - <<"ABC:">> = iolist_to_binary(join(re:split("ABC","$",[caseless]))), + 2}]))), + <<"ABC:">> = iolist_to_binary(join(re:split("ABC","$",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABC","a.c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABC","a.c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABC","a.c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABC","a.c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("AXC","a.c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("AXC","a.c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("AXC","a.c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("AXC","a.c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("AXYZC","a.*?c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("AXYZC","a.*?c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("AXYZC","a.*?c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("AXYZC","a.*?c",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.*c",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.*c",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.*c",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a.*c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("AABC","a.*c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("AABC","a.*c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("AABC","a.*c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("AABC","a.*c",[caseless]))), <<"AXYZD">> = iolist_to_binary(join(re:split("AXYZD","a.*c",[caseless, - trim]))), + trim]))), <<"AXYZD">> = iolist_to_binary(join(re:split("AXYZD","a.*c",[caseless, {parts, - 2}]))), - <<"AXYZD">> = iolist_to_binary(join(re:split("AXYZD","a.*c",[caseless]))), + 2}]))), + <<"AXYZD">> = iolist_to_binary(join(re:split("AXYZD","a.*c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABD","a[bc]d",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABD","a[bc]d",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABD","a[bc]d",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABD","a[bc]d",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ACE","a[b-d]e",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ACE","a[b-d]e",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ACE","a[b-d]e",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ACE","a[b-d]e",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[b-d]e",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[b-d]e",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[b-d]e",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[b-d]e",[caseless]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","a[b-d]e",[caseless, - trim]))), + trim]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","a[b-d]e",[caseless, {parts, - 2}]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","a[b-d]e",[caseless]))), + 2}]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","a[b-d]e",[caseless]))), <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[b-d]e",[caseless, - trim]))), + trim]))), <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[b-d]e",[caseless, {parts, - 2}]))), - <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[b-d]e",[caseless]))), + 2}]))), + <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[b-d]e",[caseless]))), <<"A">> = iolist_to_binary(join(re:split("AAC","a[b-d]",[caseless, - trim]))), + trim]))), <<"A:">> = iolist_to_binary(join(re:split("AAC","a[b-d]",[caseless, {parts, - 2}]))), - <<"A:">> = iolist_to_binary(join(re:split("AAC","a[b-d]",[caseless]))), + 2}]))), + <<"A:">> = iolist_to_binary(join(re:split("AAC","a[b-d]",[caseless]))), <<"">> = iolist_to_binary(join(re:split("A-","a[-b]",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("A-","a[-b]",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A-","a[-b]",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A-","a[-b]",[caseless]))), <<"">> = iolist_to_binary(join(re:split("A-","a[b-]",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("A-","a[b-]",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A-","a[b-]",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A-","a[b-]",[caseless]))), <<"">> = iolist_to_binary(join(re:split("A]","a]",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("A]","a]",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A]","a]",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A]","a]",[caseless]))), ok. run22() -> <<"">> = iolist_to_binary(join(re:split("A]B","a[]]b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("A]B","a[]]b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A]B","a[]]b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A]B","a[]]b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("AED","a[^bc]d",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("AED","a[^bc]d",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("AED","a[^bc]d",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("AED","a[^bc]d",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ADC","a[^-b]c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ADC","a[^-b]c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ADC","a[^-b]c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ADC","a[^-b]c",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^-b]c",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^-b]c",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^-b]c",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a[^-b]c",[caseless]))), <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[^-b]c",[caseless, - trim]))), + trim]))), <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[^-b]c",[caseless, {parts, - 2}]))), - <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[^-b]c",[caseless]))), + 2}]))), + <<"ABD">> = iolist_to_binary(join(re:split("ABD","a[^-b]c",[caseless]))), <<"A-C">> = iolist_to_binary(join(re:split("A-C","a[^-b]c",[caseless, - trim]))), + trim]))), <<"A-C">> = iolist_to_binary(join(re:split("A-C","a[^-b]c",[caseless, {parts, - 2}]))), - <<"A-C">> = iolist_to_binary(join(re:split("A-C","a[^-b]c",[caseless]))), + 2}]))), + <<"A-C">> = iolist_to_binary(join(re:split("A-C","a[^-b]c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ADC","a[^]b]c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ADC","a[^]b]c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ADC","a[^]b]c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ADC","a[^]b]c",[caseless]))), <<":C">> = iolist_to_binary(join(re:split("ABC","ab|cd",[caseless, - trim]))), + trim]))), <<":C">> = iolist_to_binary(join(re:split("ABC","ab|cd",[caseless, {parts, - 2}]))), - <<":C">> = iolist_to_binary(join(re:split("ABC","ab|cd",[caseless]))), + 2}]))), + <<":C">> = iolist_to_binary(join(re:split("ABC","ab|cd",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABCD","ab|cd",[caseless, - trim]))), + trim]))), <<":CD">> = iolist_to_binary(join(re:split("ABCD","ab|cd",[caseless, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ABCD","ab|cd",[caseless]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ABCD","ab|cd",[caseless]))), <<"D">> = iolist_to_binary(join(re:split("DEF","()ef",[caseless, - trim]))), + trim]))), <<"D::">> = iolist_to_binary(join(re:split("DEF","()ef",[caseless, {parts, - 2}]))), - <<"D::">> = iolist_to_binary(join(re:split("DEF","()ef",[caseless]))), + 2}]))), + <<"D::">> = iolist_to_binary(join(re:split("DEF","()ef",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","$b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","$b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","$b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","$b",[caseless]))), <<"A]C">> = iolist_to_binary(join(re:split("A]C","$b",[caseless, - trim]))), + trim]))), <<"A]C">> = iolist_to_binary(join(re:split("A]C","$b",[caseless, {parts, - 2}]))), - <<"A]C">> = iolist_to_binary(join(re:split("A]C","$b",[caseless]))), + 2}]))), + <<"A]C">> = iolist_to_binary(join(re:split("A]C","$b",[caseless]))), <<"B">> = iolist_to_binary(join(re:split("B","$b",[caseless, - trim]))), + trim]))), <<"B">> = iolist_to_binary(join(re:split("B","$b",[caseless, {parts, - 2}]))), - <<"B">> = iolist_to_binary(join(re:split("B","$b",[caseless]))), + 2}]))), + <<"B">> = iolist_to_binary(join(re:split("B","$b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("A(B","a\\(b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("A(B","a\\(b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A(B","a\\(b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A(B","a\\(b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("AB","a\\(*b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("AB","a\\(*b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("AB","a\\(*b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("AB","a\\(*b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("A((B","a\\(*b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("A((B","a\\(*b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("A((B","a\\(*b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("A((B","a\\(*b",[caseless]))), <<"A">> = iolist_to_binary(join(re:split("A","a\\\\b",[caseless, notbol, - trim]))), + trim]))), <<"A">> = iolist_to_binary(join(re:split("A","a\\\\b",[caseless, notbol, {parts, - 2}]))), + 2}]))), <<"A">> = iolist_to_binary(join(re:split("A","a\\\\b",[caseless, - notbol]))), + notbol]))), <<":A:A:BC">> = iolist_to_binary(join(re:split("ABC","((a))",[caseless, - trim]))), + trim]))), <<":A:A:BC">> = iolist_to_binary(join(re:split("ABC","((a))",[caseless, {parts, - 2}]))), - <<":A:A:BC">> = iolist_to_binary(join(re:split("ABC","((a))",[caseless]))), + 2}]))), + <<":A:A:BC">> = iolist_to_binary(join(re:split("ABC","((a))",[caseless]))), <<":A:C">> = iolist_to_binary(join(re:split("ABC","(a)b(c)",[caseless, - trim]))), + trim]))), <<":A:C:">> = iolist_to_binary(join(re:split("ABC","(a)b(c)",[caseless, {parts, - 2}]))), - <<":A:C:">> = iolist_to_binary(join(re:split("ABC","(a)b(c)",[caseless]))), + 2}]))), + <<":A:C:">> = iolist_to_binary(join(re:split("ABC","(a)b(c)",[caseless]))), <<"AABB">> = iolist_to_binary(join(re:split("AABBABC","a+b+c",[caseless, - trim]))), + trim]))), <<"AABB:">> = iolist_to_binary(join(re:split("AABBABC","a+b+c",[caseless, {parts, - 2}]))), - <<"AABB:">> = iolist_to_binary(join(re:split("AABBABC","a+b+c",[caseless]))), + 2}]))), + <<"AABB:">> = iolist_to_binary(join(re:split("AABBABC","a+b+c",[caseless]))), <<"AABB">> = iolist_to_binary(join(re:split("AABBABC","a{1,}b{1,}c",[caseless, - trim]))), + trim]))), <<"AABB:">> = iolist_to_binary(join(re:split("AABBABC","a{1,}b{1,}c",[caseless, {parts, - 2}]))), - <<"AABB:">> = iolist_to_binary(join(re:split("AABBABC","a{1,}b{1,}c",[caseless]))), + 2}]))), + <<"AABB:">> = iolist_to_binary(join(re:split("AABBABC","a{1,}b{1,}c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABCABC","a.+?c",[caseless, - trim]))), + trim]))), <<":ABC">> = iolist_to_binary(join(re:split("ABCABC","a.+?c",[caseless, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ABCABC","a.+?c",[caseless]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ABCABC","a.+?c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABCABC","a.*?c",[caseless, - trim]))), + trim]))), <<":ABC">> = iolist_to_binary(join(re:split("ABCABC","a.*?c",[caseless, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ABCABC","a.*?c",[caseless]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ABCABC","a.*?c",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABCABC","a.{0,5}?c",[caseless, - trim]))), + trim]))), <<":ABC">> = iolist_to_binary(join(re:split("ABCABC","a.{0,5}?c",[caseless, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ABCABC","a.{0,5}?c",[caseless]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ABCABC","a.{0,5}?c",[caseless]))), <<":B">> = iolist_to_binary(join(re:split("AB","(a+|b)*",[caseless, - trim]))), + trim]))), <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b)*",[caseless, {parts, - 2}]))), - <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b)*",[caseless]))), + 2}]))), + <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b)*",[caseless]))), <<":B">> = iolist_to_binary(join(re:split("AB","(a+|b){0,}",[caseless, - trim]))), + trim]))), <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,}",[caseless, {parts, - 2}]))), - <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,}",[caseless]))), + 2}]))), + <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,}",[caseless]))), <<":B">> = iolist_to_binary(join(re:split("AB","(a+|b)+",[caseless, - trim]))), + trim]))), <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b)+",[caseless, {parts, - 2}]))), - <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b)+",[caseless]))), + 2}]))), + <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b)+",[caseless]))), ok. run23() -> <<":B">> = iolist_to_binary(join(re:split("AB","(a+|b){1,}",[caseless, - trim]))), + trim]))), <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b){1,}",[caseless, {parts, - 2}]))), - <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b){1,}",[caseless]))), + 2}]))), + <<":B:">> = iolist_to_binary(join(re:split("AB","(a+|b){1,}",[caseless]))), <<":A::B">> = iolist_to_binary(join(re:split("AB","(a+|b)?",[caseless, - trim]))), + trim]))), <<":A:B">> = iolist_to_binary(join(re:split("AB","(a+|b)?",[caseless, {parts, - 2}]))), - <<":A::B:">> = iolist_to_binary(join(re:split("AB","(a+|b)?",[caseless]))), + 2}]))), + <<":A::B:">> = iolist_to_binary(join(re:split("AB","(a+|b)?",[caseless]))), <<":A::B">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}",[caseless, - trim]))), + trim]))), <<":A:B">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}",[caseless, {parts, - 2}]))), - <<":A::B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}",[caseless]))), + 2}]))), + <<":A::B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}",[caseless]))), <<":A::B">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}?",[caseless, - trim]))), + trim]))), <<":A:B">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}?",[caseless, {parts, - 2}]))), - <<":A::B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}?",[caseless]))), + 2}]))), + <<":A::B:">> = iolist_to_binary(join(re:split("AB","(a+|b){0,1}?",[caseless]))), <<"">> = iolist_to_binary(join(re:split("CDE","[^ab]*",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("CDE","[^ab]*",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("CDE","[^ab]*",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("CDE","[^ab]*",[caseless]))), <<":C">> = iolist_to_binary(join(re:split("ABBBCD","([abc])*d",[caseless, - trim]))), + trim]))), <<":C:">> = iolist_to_binary(join(re:split("ABBBCD","([abc])*d",[caseless, {parts, - 2}]))), - <<":C:">> = iolist_to_binary(join(re:split("ABBBCD","([abc])*d",[caseless]))), + 2}]))), + <<":C:">> = iolist_to_binary(join(re:split("ABBBCD","([abc])*d",[caseless]))), <<":A">> = iolist_to_binary(join(re:split("ABCD","([abc])*bcd",[caseless, - trim]))), + trim]))), <<":A:">> = iolist_to_binary(join(re:split("ABCD","([abc])*bcd",[caseless, {parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("ABCD","([abc])*bcd",[caseless]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("ABCD","([abc])*bcd",[caseless]))), <<"">> = iolist_to_binary(join(re:split("E","a|b|c|d|e",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("E","a|b|c|d|e",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("E","a|b|c|d|e",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("E","a|b|c|d|e",[caseless]))), <<":E">> = iolist_to_binary(join(re:split("EF","(a|b|c|d|e)f",[caseless, - trim]))), + trim]))), <<":E:">> = iolist_to_binary(join(re:split("EF","(a|b|c|d|e)f",[caseless, {parts, - 2}]))), - <<":E:">> = iolist_to_binary(join(re:split("EF","(a|b|c|d|e)f",[caseless]))), + 2}]))), + <<":E:">> = iolist_to_binary(join(re:split("EF","(a|b|c|d|e)f",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ABCDEFG","abcd*efg",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABCDEFG","abcd*efg",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABCDEFG","abcd*efg",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABCDEFG","abcd*efg",[caseless]))), <<"X:Y:Z">> = iolist_to_binary(join(re:split("XABYABBBZ","ab*",[caseless, - trim]))), + trim]))), <<"X:YABBBZ">> = iolist_to_binary(join(re:split("XABYABBBZ","ab*",[caseless, {parts, - 2}]))), - <<"X:Y:Z">> = iolist_to_binary(join(re:split("XABYABBBZ","ab*",[caseless]))), + 2}]))), + <<"X:Y:Z">> = iolist_to_binary(join(re:split("XABYABBBZ","ab*",[caseless]))), <<"X:Y:Z">> = iolist_to_binary(join(re:split("XAYABBBZ","ab*",[caseless, - trim]))), + trim]))), <<"X:YABBBZ">> = iolist_to_binary(join(re:split("XAYABBBZ","ab*",[caseless, {parts, - 2}]))), - <<"X:Y:Z">> = iolist_to_binary(join(re:split("XAYABBBZ","ab*",[caseless]))), + 2}]))), + <<"X:Y:Z">> = iolist_to_binary(join(re:split("XAYABBBZ","ab*",[caseless]))), <<"AB:CD">> = iolist_to_binary(join(re:split("ABCDE","(ab|cd)e",[caseless, - trim]))), + trim]))), <<"AB:CD:">> = iolist_to_binary(join(re:split("ABCDE","(ab|cd)e",[caseless, {parts, - 2}]))), - <<"AB:CD:">> = iolist_to_binary(join(re:split("ABCDE","(ab|cd)e",[caseless]))), + 2}]))), + <<"AB:CD:">> = iolist_to_binary(join(re:split("ABCDE","(ab|cd)e",[caseless]))), <<"">> = iolist_to_binary(join(re:split("HIJ","[abhgefdc]ij",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("HIJ","[abhgefdc]ij",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("HIJ","[abhgefdc]ij",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("HIJ","[abhgefdc]ij",[caseless]))), <<"ABCDE">> = iolist_to_binary(join(re:split("ABCDE","^(ab|cd)e",[caseless, - trim]))), + trim]))), <<"ABCDE">> = iolist_to_binary(join(re:split("ABCDE","^(ab|cd)e",[caseless, {parts, - 2}]))), - <<"ABCDE">> = iolist_to_binary(join(re:split("ABCDE","^(ab|cd)e",[caseless]))), + 2}]))), + <<"ABCDE">> = iolist_to_binary(join(re:split("ABCDE","^(ab|cd)e",[caseless]))), <<"ABCD">> = iolist_to_binary(join(re:split("ABCDEF","(abc|)ef",[caseless, - trim]))), + trim]))), <<"ABCD::">> = iolist_to_binary(join(re:split("ABCDEF","(abc|)ef",[caseless, {parts, - 2}]))), - <<"ABCD::">> = iolist_to_binary(join(re:split("ABCDEF","(abc|)ef",[caseless]))), + 2}]))), + <<"ABCD::">> = iolist_to_binary(join(re:split("ABCDEF","(abc|)ef",[caseless]))), <<"A:B">> = iolist_to_binary(join(re:split("ABCD","(a|b)c*d",[caseless, - trim]))), + trim]))), <<"A:B:">> = iolist_to_binary(join(re:split("ABCD","(a|b)c*d",[caseless, {parts, - 2}]))), - <<"A:B:">> = iolist_to_binary(join(re:split("ABCD","(a|b)c*d",[caseless]))), + 2}]))), + <<"A:B:">> = iolist_to_binary(join(re:split("ABCD","(a|b)c*d",[caseless]))), <<":A">> = iolist_to_binary(join(re:split("ABC","(ab|ab*)bc",[caseless, - trim]))), + trim]))), <<":A:">> = iolist_to_binary(join(re:split("ABC","(ab|ab*)bc",[caseless, {parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("ABC","(ab|ab*)bc",[caseless]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("ABC","(ab|ab*)bc",[caseless]))), <<":BC">> = iolist_to_binary(join(re:split("ABC","a([bc]*)c*",[caseless, - trim]))), + trim]))), <<":BC:">> = iolist_to_binary(join(re:split("ABC","a([bc]*)c*",[caseless, {parts, - 2}]))), - <<":BC:">> = iolist_to_binary(join(re:split("ABC","a([bc]*)c*",[caseless]))), + 2}]))), + <<":BC:">> = iolist_to_binary(join(re:split("ABC","a([bc]*)c*",[caseless]))), ok. run24() -> <<":BC:D">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c*d)",[caseless, - trim]))), + trim]))), <<":BC:D:">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c*d)",[caseless, {parts, - 2}]))), - <<":BC:D:">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c*d)",[caseless]))), + 2}]))), + <<":BC:D:">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c*d)",[caseless]))), <<":BC:D">> = iolist_to_binary(join(re:split("ABCD","a([bc]+)(c*d)",[caseless, - trim]))), + trim]))), <<":BC:D:">> = iolist_to_binary(join(re:split("ABCD","a([bc]+)(c*d)",[caseless, {parts, - 2}]))), - <<":BC:D:">> = iolist_to_binary(join(re:split("ABCD","a([bc]+)(c*d)",[caseless]))), + 2}]))), + <<":BC:D:">> = iolist_to_binary(join(re:split("ABCD","a([bc]+)(c*d)",[caseless]))), <<":B:CD">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c+d)",[caseless, - trim]))), + trim]))), <<":B:CD:">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c+d)",[caseless, {parts, - 2}]))), - <<":B:CD:">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c+d)",[caseless]))), + 2}]))), + <<":B:CD:">> = iolist_to_binary(join(re:split("ABCD","a([bc]*)(c+d)",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ADCDCDE","a[bcd]*dcdcde",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ADCDCDE","a[bcd]*dcdcde",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ADCDCDE","a[bcd]*dcdcde",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ADCDCDE","a[bcd]*dcdcde",[caseless]))), <<":AB">> = iolist_to_binary(join(re:split("ABC","(ab|a)b*c",[caseless, - trim]))), + trim]))), <<":AB:">> = iolist_to_binary(join(re:split("ABC","(ab|a)b*c",[caseless, {parts, - 2}]))), - <<":AB:">> = iolist_to_binary(join(re:split("ABC","(ab|a)b*c",[caseless]))), + 2}]))), + <<":AB:">> = iolist_to_binary(join(re:split("ABC","(ab|a)b*c",[caseless]))), <<":ABC:A:B:D">> = iolist_to_binary(join(re:split("ABCD","((a)(b)c)(d)",[caseless, - trim]))), + trim]))), <<":ABC:A:B:D:">> = iolist_to_binary(join(re:split("ABCD","((a)(b)c)(d)",[caseless, {parts, - 2}]))), - <<":ABC:A:B:D:">> = iolist_to_binary(join(re:split("ABCD","((a)(b)c)(d)",[caseless]))), + 2}]))), + <<":ABC:A:B:D:">> = iolist_to_binary(join(re:split("ABCD","((a)(b)c)(d)",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ALPHA","[a-zA-Z_][a-zA-Z0-9_]*",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ALPHA","[a-zA-Z_][a-zA-Z0-9_]*",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ALPHA","[a-zA-Z_][a-zA-Z0-9_]*",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ALPHA","[a-zA-Z_][a-zA-Z0-9_]*",[caseless]))), <<"A">> = iolist_to_binary(join(re:split("ABH","^a(bc+|b[eh])g|.h$",[caseless, - trim]))), + trim]))), <<"A::">> = iolist_to_binary(join(re:split("ABH","^a(bc+|b[eh])g|.h$",[caseless, {parts, - 2}]))), - <<"A::">> = iolist_to_binary(join(re:split("ABH","^a(bc+|b[eh])g|.h$",[caseless]))), + 2}]))), + <<"A::">> = iolist_to_binary(join(re:split("ABH","^a(bc+|b[eh])g|.h$",[caseless]))), <<":EFFGZ">> = iolist_to_binary(join(re:split("EFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<":EFFGZ::">> = iolist_to_binary(join(re:split("EFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<":EFFGZ::">> = iolist_to_binary(join(re:split("EFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<":EFFGZ::">> = iolist_to_binary(join(re:split("EFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<":IJ:J">> = iolist_to_binary(join(re:split("IJ","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<":IJ:J:">> = iolist_to_binary(join(re:split("IJ","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<":IJ:J:">> = iolist_to_binary(join(re:split("IJ","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<":IJ:J:">> = iolist_to_binary(join(re:split("IJ","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<"R:EFFGZ">> = iolist_to_binary(join(re:split("REFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<"R:EFFGZ::">> = iolist_to_binary(join(re:split("REFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<"R:EFFGZ::">> = iolist_to_binary(join(re:split("REFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<"R:EFFGZ::">> = iolist_to_binary(join(re:split("REFFGZ","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<"ADCDCDE">> = iolist_to_binary(join(re:split("ADCDCDE","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<"ADCDCDE">> = iolist_to_binary(join(re:split("ADCDCDE","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<"ADCDCDE">> = iolist_to_binary(join(re:split("ADCDCDE","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<"ADCDCDE">> = iolist_to_binary(join(re:split("ADCDCDE","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<"EFFG">> = iolist_to_binary(join(re:split("EFFG","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<"EFFG">> = iolist_to_binary(join(re:split("EFFG","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<"EFFG">> = iolist_to_binary(join(re:split("EFFG","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<"EFFG">> = iolist_to_binary(join(re:split("EFFG","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<"BCDD">> = iolist_to_binary(join(re:split("BCDD","(bc+d$|ef*g.|h?i(j|k))",[caseless, - trim]))), + trim]))), <<"BCDD">> = iolist_to_binary(join(re:split("BCDD","(bc+d$|ef*g.|h?i(j|k))",[caseless, {parts, - 2}]))), - <<"BCDD">> = iolist_to_binary(join(re:split("BCDD","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), + 2}]))), + <<"BCDD">> = iolist_to_binary(join(re:split("BCDD","(bc+d$|ef*g.|h?i(j|k))",[caseless]))), <<":A:A:A:A:A:A:A:A:A:A">> = iolist_to_binary(join(re:split("A","((((((((((a))))))))))",[caseless, - trim]))), + trim]))), <<":A:A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("A","((((((((((a))))))))))",[caseless, {parts, - 2}]))), - <<":A:A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("A","((((((((((a))))))))))",[caseless]))), + 2}]))), + <<":A:A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("A","((((((((((a))))))))))",[caseless]))), <<":A:A:A:A:A:A:A:A:A:A">> = iolist_to_binary(join(re:split("AA","((((((((((a))))))))))\\10",[caseless, - trim]))), + trim]))), <<":A:A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("AA","((((((((((a))))))))))\\10",[caseless, {parts, - 2}]))), - <<":A:A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("AA","((((((((((a))))))))))\\10",[caseless]))), + 2}]))), + <<":A:A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("AA","((((((((((a))))))))))\\10",[caseless]))), <<":A:A:A:A:A:A:A:A:A">> = iolist_to_binary(join(re:split("A","(((((((((a)))))))))",[caseless, - trim]))), + trim]))), <<":A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("A","(((((((((a)))))))))",[caseless, {parts, - 2}]))), - <<":A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("A","(((((((((a)))))))))",[caseless]))), + 2}]))), + <<":A:A:A:A:A:A:A:A:A:">> = iolist_to_binary(join(re:split("A","(((((((((a)))))))))",[caseless]))), <<":A">> = iolist_to_binary(join(re:split("A","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a))))))))))",[caseless, - trim]))), + trim]))), <<":A:">> = iolist_to_binary(join(re:split("A","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a))))))))))",[caseless, {parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("A","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a))))))))))",[caseless]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("A","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a))))))))))",[caseless]))), <<":C">> = iolist_to_binary(join(re:split("C","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a|b|c))))))))))",[caseless, - trim]))), + trim]))), <<":C:">> = iolist_to_binary(join(re:split("C","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a|b|c))))))))))",[caseless, {parts, - 2}]))), - <<":C:">> = iolist_to_binary(join(re:split("C","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a|b|c))))))))))",[caseless]))), + 2}]))), + <<":C:">> = iolist_to_binary(join(re:split("C","(?:(?:(?:(?:(?:(?:(?:(?:(?:(a|b|c))))))))))",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","multiple words of text",[caseless]))), <<"AA">> = iolist_to_binary(join(re:split("AA","multiple words of text",[caseless, - trim]))), + trim]))), <<"AA">> = iolist_to_binary(join(re:split("AA","multiple words of text",[caseless, {parts, - 2}]))), - <<"AA">> = iolist_to_binary(join(re:split("AA","multiple words of text",[caseless]))), + 2}]))), + <<"AA">> = iolist_to_binary(join(re:split("AA","multiple words of text",[caseless]))), <<"UH-UH">> = iolist_to_binary(join(re:split("UH-UH","multiple words of text",[caseless, - trim]))), + trim]))), <<"UH-UH">> = iolist_to_binary(join(re:split("UH-UH","multiple words of text",[caseless, {parts, - 2}]))), - <<"UH-UH">> = iolist_to_binary(join(re:split("UH-UH","multiple words of text",[caseless]))), + 2}]))), + <<"UH-UH">> = iolist_to_binary(join(re:split("UH-UH","multiple words of text",[caseless]))), <<":, YEAH">> = iolist_to_binary(join(re:split("MULTIPLE WORDS, YEAH","multiple words",[caseless, - trim]))), + trim]))), <<":, YEAH">> = iolist_to_binary(join(re:split("MULTIPLE WORDS, YEAH","multiple words",[caseless, {parts, - 2}]))), - <<":, YEAH">> = iolist_to_binary(join(re:split("MULTIPLE WORDS, YEAH","multiple words",[caseless]))), + 2}]))), + <<":, YEAH">> = iolist_to_binary(join(re:split("MULTIPLE WORDS, YEAH","multiple words",[caseless]))), <<":AB:DE">> = iolist_to_binary(join(re:split("ABCDE","(.*)c(.*)",[caseless, - trim]))), + trim]))), <<":AB:DE:">> = iolist_to_binary(join(re:split("ABCDE","(.*)c(.*)",[caseless, {parts, - 2}]))), - <<":AB:DE:">> = iolist_to_binary(join(re:split("ABCDE","(.*)c(.*)",[caseless]))), + 2}]))), + <<":AB:DE:">> = iolist_to_binary(join(re:split("ABCDE","(.*)c(.*)",[caseless]))), <<":A:B">> = iolist_to_binary(join(re:split("(A, B)","\\((.*), (.*)\\)",[caseless, - trim]))), + trim]))), <<":A:B:">> = iolist_to_binary(join(re:split("(A, B)","\\((.*), (.*)\\)",[caseless, {parts, - 2}]))), - <<":A:B:">> = iolist_to_binary(join(re:split("(A, B)","\\((.*), (.*)\\)",[caseless]))), + 2}]))), + <<":A:B:">> = iolist_to_binary(join(re:split("(A, B)","\\((.*), (.*)\\)",[caseless]))), ok. run25() -> <<"">> = iolist_to_binary(join(re:split("ABCD","abcd",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ABCD","abcd",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ABCD","abcd",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ABCD","abcd",[caseless]))), <<":BC">> = iolist_to_binary(join(re:split("ABCD","a(bc)d",[caseless, - trim]))), + trim]))), <<":BC:">> = iolist_to_binary(join(re:split("ABCD","a(bc)d",[caseless, {parts, - 2}]))), - <<":BC:">> = iolist_to_binary(join(re:split("ABCD","a(bc)d",[caseless]))), + 2}]))), + <<":BC:">> = iolist_to_binary(join(re:split("ABCD","a(bc)d",[caseless]))), <<"">> = iolist_to_binary(join(re:split("AC","a[-]?c",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("AC","a[-]?c",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("AC","a[-]?c",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("AC","a[-]?c",[caseless]))), <<":ABC">> = iolist_to_binary(join(re:split("ABCABC","(abc)\\1",[caseless, - trim]))), + trim]))), <<":ABC:">> = iolist_to_binary(join(re:split("ABCABC","(abc)\\1",[caseless, {parts, - 2}]))), - <<":ABC:">> = iolist_to_binary(join(re:split("ABCABC","(abc)\\1",[caseless]))), + 2}]))), + <<":ABC:">> = iolist_to_binary(join(re:split("ABCABC","(abc)\\1",[caseless]))), <<":ABC">> = iolist_to_binary(join(re:split("ABCABC","([a-c]*)\\1",[caseless, - trim]))), + trim]))), <<":ABC:">> = iolist_to_binary(join(re:split("ABCABC","([a-c]*)\\1",[caseless, {parts, - 2}]))), - <<":ABC:">> = iolist_to_binary(join(re:split("ABCABC","([a-c]*)\\1",[caseless]))), - <<"ab">> = iolist_to_binary(join(re:split("abad","a(?!b).",[trim]))), + 2}]))), + <<":ABC:">> = iolist_to_binary(join(re:split("ABCABC","([a-c]*)\\1",[caseless]))), + <<"ab">> = iolist_to_binary(join(re:split("abad","a(?!b).",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?!b).",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?!b).",[]))), - <<"ab">> = iolist_to_binary(join(re:split("abad","a(?=d).",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?!b).",[]))), + <<"ab">> = iolist_to_binary(join(re:split("abad","a(?=d).",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?=d).",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?=d).",[]))), - <<"ab">> = iolist_to_binary(join(re:split("abad","a(?=c|d).",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?=d).",[]))), + <<"ab">> = iolist_to_binary(join(re:split("abad","a(?=c|d).",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?=c|d).",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?=c|d).",[]))), - <<":e">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)(.)",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abad","a(?=c|d).",[]))), + <<":e">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)(.)",[]))), - <<":e">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)*(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)(.)",[]))), + <<":e">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)*(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)*(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)*(.)",[]))), - <<":e">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)+?(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)*(.)",[]))), + <<":e">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)+?(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)+?(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)+?(.)",[]))), - <<":d:bcdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+?(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("ace","a(?:b|c|d)+?(.)",[]))), + <<":d:bcdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+?(.)",[trim]))), <<":d:bcdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+?(.)",[{parts, - 2}]))), - <<":d:bcdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+?(.)",[]))), - <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+(.)",[trim]))), + 2}]))), + <<":d:bcdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+?(.)",[]))), + <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+(.)",[]))), - <<":b:cdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){2}(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d)+(.)",[]))), + <<":b:cdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){2}(.)",[trim]))), <<":b:cdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){2}(.)",[{parts, - 2}]))), - <<":b:cdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){2}(.)",[]))), - <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}(.)",[trim]))), + 2}]))), + <<":b:cdbe">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){2}(.)",[]))), + <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}(.)",[trim]))), <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}(.)",[{parts, - 2}]))), - <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}(.)",[]))), - <<":d:be">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}?(.)",[trim]))), + 2}]))), + <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}(.)",[]))), + <<":d:be">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}?(.)",[trim]))), <<":d:be">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}?(.)",[{parts, - 2}]))), - <<":d:be">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}?(.)",[]))), - <<":bar:foo:bar">> = iolist_to_binary(join(re:split("foobar","((foo)|(bar))*",[trim]))), + 2}]))), + <<":d:be">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){4,5}?(.)",[]))), + <<":bar:foo:bar">> = iolist_to_binary(join(re:split("foobar","((foo)|(bar))*",[trim]))), <<":bar:foo:bar:">> = iolist_to_binary(join(re:split("foobar","((foo)|(bar))*",[{parts, - 2}]))), - <<":bar:foo:bar:">> = iolist_to_binary(join(re:split("foobar","((foo)|(bar))*",[]))), - <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}(.)",[trim]))), + 2}]))), + <<":bar:foo:bar:">> = iolist_to_binary(join(re:split("foobar","((foo)|(bar))*",[]))), + <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}(.)",[]))), - <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}?(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}(.)",[]))), + <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}?(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}?(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}?(.)",[]))), - <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){6,7}?(.)",[]))), + <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}(.)",[]))), - <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}?(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}(.)",[]))), + <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}?(.)",[trim]))), <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}?(.)",[{parts, - 2}]))), - <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}?(.)",[]))), + 2}]))), + <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,6}?(.)",[]))), ok. run26() -> - <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}(.)",[trim]))), + <<":e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}(.)",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}(.)",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}(.)",[]))), - <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}?(.)",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}(.)",[]))), + <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}?(.)",[trim]))), <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}?(.)",[{parts, - 2}]))), - <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}?(.)",[]))), - <<":c:e">> = iolist_to_binary(join(re:split("ace","a(?:b|(c|e){1,2}?|d)+?(.)",[trim]))), + 2}]))), + <<":b:e">> = iolist_to_binary(join(re:split("acdbcdbe","a(?:b|c|d){5,7}?(.)",[]))), + <<":c:e">> = iolist_to_binary(join(re:split("ace","a(?:b|(c|e){1,2}?|d)+?(.)",[trim]))), <<":c:e:">> = iolist_to_binary(join(re:split("ace","a(?:b|(c|e){1,2}?|d)+?(.)",[{parts, - 2}]))), - <<":c:e:">> = iolist_to_binary(join(re:split("ace","a(?:b|(c|e){1,2}?|d)+?(.)",[]))), - <<":A">> = iolist_to_binary(join(re:split("AB","^(.+)?B",[trim]))), + 2}]))), + <<":c:e:">> = iolist_to_binary(join(re:split("ace","a(?:b|(c|e){1,2}?|d)+?(.)",[]))), + <<":A">> = iolist_to_binary(join(re:split("AB","^(.+)?B",[trim]))), <<":A:">> = iolist_to_binary(join(re:split("AB","^(.+)?B",[{parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("AB","^(.+)?B",[]))), - <<":.">> = iolist_to_binary(join(re:split(".","^([^a-z])|(\\^)$",[trim]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("AB","^(.+)?B",[]))), + <<":.">> = iolist_to_binary(join(re:split(".","^([^a-z])|(\\^)$",[trim]))), <<":.::">> = iolist_to_binary(join(re:split(".","^([^a-z])|(\\^)$",[{parts, - 2}]))), - <<":.::">> = iolist_to_binary(join(re:split(".","^([^a-z])|(\\^)$",[]))), - <<":OUT">> = iolist_to_binary(join(re:split("<&OUT","^[<>]&",[trim]))), + 2}]))), + <<":.::">> = iolist_to_binary(join(re:split(".","^([^a-z])|(\\^)$",[]))), + <<":OUT">> = iolist_to_binary(join(re:split("<&OUT","^[<>]&",[trim]))), <<":OUT">> = iolist_to_binary(join(re:split("<&OUT","^[<>]&",[{parts, - 2}]))), - <<":OUT">> = iolist_to_binary(join(re:split("<&OUT","^[<>]&",[]))), - <<":aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<":OUT">> = iolist_to_binary(join(re:split("<&OUT","^[<>]&",[]))), + <<":aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a\\1?){4}$",[trim]))), + 2}]))), + <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a\\1?){4}$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a\\1?){4}$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a\\1?){4}$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a\\1?){4}$",[]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a\\1?){4}$",[]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","^(a\\1?){4}$",[trim]))), <<"AB">> = iolist_to_binary(join(re:split("AB","^(a\\1?){4}$",[{parts, - 2}]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","^(a\\1?){4}$",[]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","^(a\\1?){4}$",[]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a\\1?){4}$",[]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[trim]))), <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[]))), - <<":aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a(?(1)\\1)){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a\\1?){4}$",[]))), + <<":aaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a(?(1)\\1)){4}$",[trim]))), <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a(?(1)\\1)){4}$",[{parts, - 2}]))), - <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a(?(1)\\1)){4}$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a(?(1)\\1)){4}$",[trim]))), + 2}]))), + <<":aaaa:">> = iolist_to_binary(join(re:split("aaaaaaaaaa","^(a(?(1)\\1)){4}$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a(?(1)\\1)){4}$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a(?(1)\\1)){4}$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a(?(1)\\1)){4}$",[]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a(?(1)\\1)){4}$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a(?(1)\\1)){4}$",[]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a(?(1)\\1)){4}$",[trim]))), <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a(?(1)\\1)){4}$",[{parts, - 2}]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a(?(1)\\1)){4}$",[]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a(?(1)\\1)){4}$",[trim]))), + 2}]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a(?(1)\\1)){4}$",[]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a(?(1)\\1)){4}$",[trim]))), <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a(?(1)\\1)){4}$",[{parts, - 2}]))), - <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a(?(1)\\1)){4}$",[]))), - <<":f:o:o:b:a:r">> = iolist_to_binary(join(re:split("foobar","(?:(f)(o)(o)|(b)(a)(r))*",[trim]))), + 2}]))), + <<"aaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaa","^(a(?(1)\\1)){4}$",[]))), + <<":f:o:o:b:a:r">> = iolist_to_binary(join(re:split("foobar","(?:(f)(o)(o)|(b)(a)(r))*",[trim]))), <<":f:o:o:b:a:r:">> = iolist_to_binary(join(re:split("foobar","(?:(f)(o)(o)|(b)(a)(r))*",[{parts, - 2}]))), - <<":f:o:o:b:a:r:">> = iolist_to_binary(join(re:split("foobar","(?:(f)(o)(o)|(b)(a)(r))*",[]))), - <<"a">> = iolist_to_binary(join(re:split("ab","(?<=a)b",[trim]))), + 2}]))), + <<":f:o:o:b:a:r:">> = iolist_to_binary(join(re:split("foobar","(?:(f)(o)(o)|(b)(a)(r))*",[]))), + <<"a">> = iolist_to_binary(join(re:split("ab","(?<=a)b",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("ab","(?<=a)b",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("ab","(?<=a)b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a)b",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("ab","(?<=a)b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a)b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a)b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a)b",[]))), - <<"cb">> = iolist_to_binary(join(re:split("cb","(?<=a)b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=a)b",[]))), + <<"cb">> = iolist_to_binary(join(re:split("cb","(?<=a)b",[trim]))), <<"cb">> = iolist_to_binary(join(re:split("cb","(?<=a)b",[{parts, - 2}]))), - <<"cb">> = iolist_to_binary(join(re:split("cb","(?<=a)b",[]))), - <<"b">> = iolist_to_binary(join(re:split("b","(?<=a)b",[trim]))), + 2}]))), + <<"cb">> = iolist_to_binary(join(re:split("cb","(?<=a)b",[]))), + <<"b">> = iolist_to_binary(join(re:split("b","(?<=a)b",[trim]))), <<"b">> = iolist_to_binary(join(re:split("b","(?<=a)b",[{parts, - 2}]))), - <<"b">> = iolist_to_binary(join(re:split("b","(?<=a)b",[]))), - <<"a">> = iolist_to_binary(join(re:split("ab","(?<!c)b",[trim]))), + 2}]))), + <<"b">> = iolist_to_binary(join(re:split("b","(?<=a)b",[]))), + <<"a">> = iolist_to_binary(join(re:split("ab","(?<!c)b",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("ab","(?<!c)b",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("ab","(?<!c)b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","(?<!c)b",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("ab","(?<!c)b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","(?<!c)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("b","(?<!c)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("b","(?<!c)b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","(?<!c)b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("b","(?<!c)b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","(?<!c)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("b","(?<!c)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("b","(?<!c)b",[]))), - <<"">> = iolist_to_binary(join(re:split("aba","(?:..)*a",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("b","(?<!c)b",[]))), + <<"">> = iolist_to_binary(join(re:split("aba","(?:..)*a",[trim]))), <<":">> = iolist_to_binary(join(re:split("aba","(?:..)*a",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aba","(?:..)*a",[]))), - <<":b">> = iolist_to_binary(join(re:split("aba","(?:..)*?a",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aba","(?:..)*a",[]))), + <<":b">> = iolist_to_binary(join(re:split("aba","(?:..)*?a",[trim]))), <<":ba">> = iolist_to_binary(join(re:split("aba","(?:..)*?a",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("aba","(?:..)*?a",[]))), - <<":b:c">> = iolist_to_binary(join(re:split("abc","^(?:b|a(?=(.)))*\\1",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("aba","(?:..)*?a",[]))), + <<":b:c">> = iolist_to_binary(join(re:split("abc","^(?:b|a(?=(.)))*\\1",[trim]))), <<":b:c">> = iolist_to_binary(join(re:split("abc","^(?:b|a(?=(.)))*\\1",[{parts, - 2}]))), - <<":b:c">> = iolist_to_binary(join(re:split("abc","^(?:b|a(?=(.)))*\\1",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(){3,5}",[trim]))), + 2}]))), + <<":b:c">> = iolist_to_binary(join(re:split("abc","^(?:b|a(?=(.)))*\\1",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(){3,5}",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^(){3,5}",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(){3,5}",[]))), - <<":a">> = iolist_to_binary(join(re:split("aax","^(a+)*ax",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(){3,5}",[]))), + <<":a">> = iolist_to_binary(join(re:split("aax","^(a+)*ax",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aax","^(a+)*ax",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aax","^(a+)*ax",[]))), - <<":a:a">> = iolist_to_binary(join(re:split("aax","^((a|b)+)*ax",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aax","^(a+)*ax",[]))), + <<":a:a">> = iolist_to_binary(join(re:split("aax","^((a|b)+)*ax",[trim]))), <<":a:a:">> = iolist_to_binary(join(re:split("aax","^((a|b)+)*ax",[{parts, - 2}]))), - <<":a:a:">> = iolist_to_binary(join(re:split("aax","^((a|b)+)*ax",[]))), - <<":a:a">> = iolist_to_binary(join(re:split("aax","^((a|bc)+)*ax",[trim]))), + 2}]))), + <<":a:a:">> = iolist_to_binary(join(re:split("aax","^((a|b)+)*ax",[]))), + <<":a:a">> = iolist_to_binary(join(re:split("aax","^((a|bc)+)*ax",[trim]))), <<":a:a:">> = iolist_to_binary(join(re:split("aax","^((a|bc)+)*ax",[{parts, - 2}]))), - <<":a:a:">> = iolist_to_binary(join(re:split("aax","^((a|bc)+)*ax",[]))), - <<"c">> = iolist_to_binary(join(re:split("cab","(a|x)*ab",[trim]))), + 2}]))), + <<":a:a:">> = iolist_to_binary(join(re:split("aax","^((a|bc)+)*ax",[]))), + <<"c">> = iolist_to_binary(join(re:split("cab","(a|x)*ab",[trim]))), <<"c::">> = iolist_to_binary(join(re:split("cab","(a|x)*ab",[{parts, - 2}]))), - <<"c::">> = iolist_to_binary(join(re:split("cab","(a|x)*ab",[]))), - <<"c">> = iolist_to_binary(join(re:split("cab","(a)*ab",[trim]))), + 2}]))), + <<"c::">> = iolist_to_binary(join(re:split("cab","(a|x)*ab",[]))), + <<"c">> = iolist_to_binary(join(re:split("cab","(a)*ab",[trim]))), <<"c::">> = iolist_to_binary(join(re:split("cab","(a)*ab",[{parts, - 2}]))), - <<"c::">> = iolist_to_binary(join(re:split("cab","(a)*ab",[]))), + 2}]))), + <<"c::">> = iolist_to_binary(join(re:split("cab","(a)*ab",[]))), ok. run27() -> - <<"">> = iolist_to_binary(join(re:split("ab","(?:(?i)a)b",[trim]))), + <<"">> = iolist_to_binary(join(re:split("ab","(?:(?i)a)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab","(?:(?i)a)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","(?:(?i)a)b",[]))), - <<":a">> = iolist_to_binary(join(re:split("ab","((?i)a)b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","(?:(?i)a)b",[]))), + <<":a">> = iolist_to_binary(join(re:split("ab","((?i)a)b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ab","((?i)a)b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ab","((?i)a)b",[]))), - <<"">> = iolist_to_binary(join(re:split("Ab","(?:(?i)a)b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ab","((?i)a)b",[]))), + <<"">> = iolist_to_binary(join(re:split("Ab","(?:(?i)a)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("Ab","(?:(?i)a)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("Ab","(?:(?i)a)b",[]))), - <<":A">> = iolist_to_binary(join(re:split("Ab","((?i)a)b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("Ab","(?:(?i)a)b",[]))), + <<":A">> = iolist_to_binary(join(re:split("Ab","((?i)a)b",[trim]))), <<":A:">> = iolist_to_binary(join(re:split("Ab","((?i)a)b",[{parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("Ab","((?i)a)b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?i)a)b",[trim]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("Ab","((?i)a)b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?i)a)b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?i)a)b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?i)a)b",[]))), - <<"cb">> = iolist_to_binary(join(re:split("cb","(?:(?i)a)b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?i)a)b",[]))), + <<"cb">> = iolist_to_binary(join(re:split("cb","(?:(?i)a)b",[trim]))), <<"cb">> = iolist_to_binary(join(re:split("cb","(?:(?i)a)b",[{parts, - 2}]))), - <<"cb">> = iolist_to_binary(join(re:split("cb","(?:(?i)a)b",[]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(?i)a)b",[trim]))), + 2}]))), + <<"cb">> = iolist_to_binary(join(re:split("cb","(?:(?i)a)b",[]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(?i)a)b",[trim]))), <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(?i)a)b",[{parts, - 2}]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(?i)a)b",[]))), - <<"">> = iolist_to_binary(join(re:split("ab","(?i:a)b",[trim]))), + 2}]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?:(?i)a)b",[]))), + <<"">> = iolist_to_binary(join(re:split("ab","(?i:a)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab","(?i:a)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","(?i:a)b",[]))), - <<":a">> = iolist_to_binary(join(re:split("ab","((?i:a))b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","(?i:a)b",[]))), + <<":a">> = iolist_to_binary(join(re:split("ab","((?i:a))b",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ab","((?i:a))b",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ab","((?i:a))b",[]))), - <<"">> = iolist_to_binary(join(re:split("Ab","(?i:a)b",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ab","((?i:a))b",[]))), + <<"">> = iolist_to_binary(join(re:split("Ab","(?i:a)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("Ab","(?i:a)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("Ab","(?i:a)b",[]))), - <<":A">> = iolist_to_binary(join(re:split("Ab","((?i:a))b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("Ab","(?i:a)b",[]))), + <<":A">> = iolist_to_binary(join(re:split("Ab","((?i:a))b",[trim]))), <<":A:">> = iolist_to_binary(join(re:split("Ab","((?i:a))b",[{parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("Ab","((?i:a))b",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i:a)b",[trim]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("Ab","((?i:a))b",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i:a)b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i:a)b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i:a)b",[]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i:a)b",[]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[trim]))), <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[{parts, - 2}]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[trim]))), + 2}]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[trim]))), <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[{parts, - 2}]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[]))), + 2}]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:a)b",[]))), <<"">> = iolist_to_binary(join(re:split("ab","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ab","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","(?:(?-i)a)b",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("ab","((?-i)a)b",[caseless, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("ab","((?-i)a)b",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ab","((?-i)a)b",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ab","((?-i)a)b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aB","(?:(?-i)a)b",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i)a)b",[caseless]))), ok. run28() -> <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?:(?-i)a)b",[caseless]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?:(?-i)a)b",[caseless]))), <<"AB">> = iolist_to_binary(join(re:split("AB","(?:(?-i)a)b",[caseless, - trim]))), + trim]))), <<"AB">> = iolist_to_binary(join(re:split("AB","(?:(?-i)a)b",[caseless, {parts, - 2}]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","(?:(?-i)a)b",[caseless]))), + 2}]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","(?:(?-i)a)b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("ab","(?-i:a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ab","(?-i:a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","(?-i:a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","(?-i:a)b",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("ab","((?-i:a))b",[caseless, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("ab","((?-i:a))b",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ab","((?-i:a))b",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ab","((?-i:a))b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless]))), <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless, - trim]))), + trim]))), <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless, {parts, - 2}]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless]))), + 2}]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless, - trim]))), + trim]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless, {parts, - 2}]))), - <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless]))), + 2}]))), + <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless]))), <<"">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aB","(?-i:a)b",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aB","((?-i:a))b",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?-i:a)b",[caseless]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless, - trim]))), + trim]))), <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless, {parts, - 2}]))), - <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless]))), + 2}]))), + <<"Ab">> = iolist_to_binary(join(re:split("Ab","(?-i:a)b",[caseless]))), <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless, - trim]))), + trim]))), <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless, {parts, - 2}]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless]))), + 2}]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","(?-i:a)b",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?-i:a.))b",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?-i:a.))b",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?-i:a.))b",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?-i:a.))b",[caseless]))), <<"AB">> = iolist_to_binary(join(re:split("AB","((?-i:a.))b",[caseless, - trim]))), + trim]))), <<"AB">> = iolist_to_binary(join(re:split("AB","((?-i:a.))b",[caseless, {parts, - 2}]))), - <<"AB">> = iolist_to_binary(join(re:split("AB","((?-i:a.))b",[caseless]))), + 2}]))), + <<"AB">> = iolist_to_binary(join(re:split("AB","((?-i:a.))b",[caseless]))), <<"a B">> = iolist_to_binary(join(re:split("a -B","((?-i:a.))b",[caseless,trim]))), +B","((?-i:a.))b",[caseless,trim]))), <<"a B">> = iolist_to_binary(join(re:split("a -B","((?-i:a.))b",[caseless,{parts,2}]))), +B","((?-i:a.))b",[caseless,{parts,2}]))), <<"a B">> = iolist_to_binary(join(re:split("a -B","((?-i:a.))b",[caseless]))), +B","((?-i:a.))b",[caseless]))), <<":a ">> = iolist_to_binary(join(re:split("a -B","((?s-i:a.))b",[caseless,trim]))), +B","((?s-i:a.))b",[caseless,trim]))), <<":a :">> = iolist_to_binary(join(re:split("a -B","((?s-i:a.))b",[caseless,{parts,2}]))), +B","((?s-i:a.))b",[caseless,{parts,2}]))), <<":a :">> = iolist_to_binary(join(re:split("a -B","((?s-i:a.))b",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("cabbbb","(?:c|d)(?:)(?:a(?:)(?:b)(?:b(?:))(?:b(?:)(?:b)))",[trim]))), +B","((?s-i:a.))b",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("cabbbb","(?:c|d)(?:)(?:a(?:)(?:b)(?:b(?:))(?:b(?:)(?:b)))",[trim]))), <<":">> = iolist_to_binary(join(re:split("cabbbb","(?:c|d)(?:)(?:a(?:)(?:b)(?:b(?:))(?:b(?:)(?:b)))",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("cabbbb","(?:c|d)(?:)(?:a(?:)(?:b)(?:b(?:))(?:b(?:)(?:b)))",[]))), - <<"">> = iolist_to_binary(join(re:split("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","(?:c|d)(?:)(?:aaaaaaaa(?:)(?:bbbbbbbb)(?:bbbbbbbb(?:))(?:bbbbbbbb(?:)(?:bbbbbbbb)))",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("cabbbb","(?:c|d)(?:)(?:a(?:)(?:b)(?:b(?:))(?:b(?:)(?:b)))",[]))), + <<"">> = iolist_to_binary(join(re:split("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","(?:c|d)(?:)(?:aaaaaaaa(?:)(?:bbbbbbbb)(?:bbbbbbbb(?:))(?:bbbbbbbb(?:)(?:bbbbbbbb)))",[trim]))), <<":">> = iolist_to_binary(join(re:split("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","(?:c|d)(?:)(?:aaaaaaaa(?:)(?:bbbbbbbb)(?:bbbbbbbb(?:))(?:bbbbbbbb(?:)(?:bbbbbbbb)))",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","(?:c|d)(?:)(?:aaaaaaaa(?:)(?:bbbbbbbb)(?:bbbbbbbb(?:))(?:bbbbbbbb(?:)(?:bbbbbbbb)))",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("caaaaaaaabbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb","(?:c|d)(?:)(?:aaaaaaaa(?:)(?:bbbbbbbb)(?:bbbbbbbb(?:))(?:bbbbbbbb(?:)(?:bbbbbbbb)))",[]))), <<":Ab">> = iolist_to_binary(join(re:split("Ab4ab","(ab)\\d\\1",[caseless, - trim]))), + trim]))), <<":Ab:">> = iolist_to_binary(join(re:split("Ab4ab","(ab)\\d\\1",[caseless, {parts, - 2}]))), - <<":Ab:">> = iolist_to_binary(join(re:split("Ab4ab","(ab)\\d\\1",[caseless]))), + 2}]))), + <<":Ab:">> = iolist_to_binary(join(re:split("Ab4ab","(ab)\\d\\1",[caseless]))), <<":ab">> = iolist_to_binary(join(re:split("ab4Ab","(ab)\\d\\1",[caseless, - trim]))), + trim]))), <<":ab:">> = iolist_to_binary(join(re:split("ab4Ab","(ab)\\d\\1",[caseless, {parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("ab4Ab","(ab)\\d\\1",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("foobar1234baz","foo\\w*\\d{4}baz",[trim]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("ab4Ab","(ab)\\d\\1",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("foobar1234baz","foo\\w*\\d{4}baz",[trim]))), <<":">> = iolist_to_binary(join(re:split("foobar1234baz","foo\\w*\\d{4}baz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("foobar1234baz","foo\\w*\\d{4}baz",[]))), - <<":~~">> = iolist_to_binary(join(re:split("x~~","x(~~)*(?:(?:F)?)?",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("foobar1234baz","foo\\w*\\d{4}baz",[]))), + <<":~~">> = iolist_to_binary(join(re:split("x~~","x(~~)*(?:(?:F)?)?",[trim]))), <<":~~:">> = iolist_to_binary(join(re:split("x~~","x(~~)*(?:(?:F)?)?",[{parts, - 2}]))), - <<":~~:">> = iolist_to_binary(join(re:split("x~~","x(~~)*(?:(?:F)?)?",[]))), - <<"">> = iolist_to_binary(join(re:split("aaac","^a(?#xxx){3}c",[trim]))), + 2}]))), + <<":~~:">> = iolist_to_binary(join(re:split("x~~","x(~~)*(?:(?:F)?)?",[]))), + <<"">> = iolist_to_binary(join(re:split("aaac","^a(?#xxx){3}c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaac","^a(?#xxx){3}c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaac","^a(?#xxx){3}c",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaac","^a(?#xxx){3}c",[]))), ok. run29() -> <<"">> = iolist_to_binary(join(re:split("aaac","^a (?#xxx) (?#yyy) {3}c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aaac","^a (?#xxx) (?#yyy) {3}c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaac","^a (?#xxx) (?#yyy) {3}c",[extended]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<![cd])b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaac","^a (?#xxx) (?#yyy) {3}c",[extended]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<![cd])b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<![cd])b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<![cd])b",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<![cd])b",[]))), <<"B B">> = iolist_to_binary(join(re:split("B -B","(?<![cd])b",[trim]))), +B","(?<![cd])b",[trim]))), <<"B B">> = iolist_to_binary(join(re:split("B -B","(?<![cd])b",[{parts,2}]))), +B","(?<![cd])b",[{parts,2}]))), <<"B B">> = iolist_to_binary(join(re:split("B -B","(?<![cd])b",[]))), - <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","(?<![cd])b",[trim]))), +B","(?<![cd])b",[]))), + <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","(?<![cd])b",[trim]))), <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","(?<![cd])b",[{parts, - 2}]))), - <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","(?<![cd])b",[]))), - <<"db::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<![cd])[ab]",[trim]))), + 2}]))), + <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","(?<![cd])b",[]))), + <<"db::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<![cd])[ab]",[trim]))), <<"db:acb">> = iolist_to_binary(join(re:split("dbaacb","(?<![cd])[ab]",[{parts, - 2}]))), - <<"db::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<![cd])[ab]",[]))), - <<"db::::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<!(c|d))[ab]",[trim]))), + 2}]))), + <<"db::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<![cd])[ab]",[]))), + <<"db::::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<!(c|d))[ab]",[trim]))), <<"db::acb">> = iolist_to_binary(join(re:split("dbaacb","(?<!(c|d))[ab]",[{parts, - 2}]))), - <<"db::::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<!(c|d))[ab]",[]))), - <<"cdacc">> = iolist_to_binary(join(re:split("cdaccb","(?<!cd)[ab]",[trim]))), + 2}]))), + <<"db::::cb">> = iolist_to_binary(join(re:split("dbaacb","(?<!(c|d))[ab]",[]))), + <<"cdacc">> = iolist_to_binary(join(re:split("cdaccb","(?<!cd)[ab]",[trim]))), <<"cdacc:">> = iolist_to_binary(join(re:split("cdaccb","(?<!cd)[ab]",[{parts, - 2}]))), - <<"cdacc:">> = iolist_to_binary(join(re:split("cdaccb","(?<!cd)[ab]",[]))), - <<"">> = iolist_to_binary(join(re:split("","^(?:a?b?)*$",[trim]))), + 2}]))), + <<"cdacc:">> = iolist_to_binary(join(re:split("cdaccb","(?<!cd)[ab]",[]))), + <<"">> = iolist_to_binary(join(re:split("","^(?:a?b?)*$",[trim]))), <<"">> = iolist_to_binary(join(re:split("","^(?:a?b?)*$",[{parts, - 2}]))), - <<"">> = iolist_to_binary(join(re:split("","^(?:a?b?)*$",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^(?:a?b?)*$",[trim]))), + 2}]))), + <<"">> = iolist_to_binary(join(re:split("","^(?:a?b?)*$",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^(?:a?b?)*$",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","^(?:a?b?)*$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","^(?:a?b?)*$",[]))), - <<"">> = iolist_to_binary(join(re:split("ab","^(?:a?b?)*$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","^(?:a?b?)*$",[]))), + <<"">> = iolist_to_binary(join(re:split("ab","^(?:a?b?)*$",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab","^(?:a?b?)*$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","^(?:a?b?)*$",[]))), - <<"">> = iolist_to_binary(join(re:split("aaa","^(?:a?b?)*$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","^(?:a?b?)*$",[]))), + <<"">> = iolist_to_binary(join(re:split("aaa","^(?:a?b?)*$",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaa","^(?:a?b?)*$",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaa","^(?:a?b?)*$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:a?b?)*$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaa","^(?:a?b?)*$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:a?b?)*$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:a?b?)*$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:a?b?)*$",[]))), - <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","^(?:a?b?)*$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:a?b?)*$",[]))), + <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","^(?:a?b?)*$",[trim]))), <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","^(?:a?b?)*$",[{parts, - 2}]))), - <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","^(?:a?b?)*$",[]))), - <<"a--">> = iolist_to_binary(join(re:split("a--","^(?:a?b?)*$",[trim]))), + 2}]))), + <<"dbcb">> = iolist_to_binary(join(re:split("dbcb","^(?:a?b?)*$",[]))), + <<"a--">> = iolist_to_binary(join(re:split("a--","^(?:a?b?)*$",[trim]))), <<"a--">> = iolist_to_binary(join(re:split("a--","^(?:a?b?)*$",[{parts, - 2}]))), - <<"a--">> = iolist_to_binary(join(re:split("a--","^(?:a?b?)*$",[]))), - <<"aa--">> = iolist_to_binary(join(re:split("aa--","^(?:a?b?)*$",[trim]))), + 2}]))), + <<"a--">> = iolist_to_binary(join(re:split("a--","^(?:a?b?)*$",[]))), + <<"aa--">> = iolist_to_binary(join(re:split("aa--","^(?:a?b?)*$",[trim]))), <<"aa--">> = iolist_to_binary(join(re:split("aa--","^(?:a?b?)*$",[{parts, - 2}]))), - <<"aa--">> = iolist_to_binary(join(re:split("aa--","^(?:a?b?)*$",[]))), + 2}]))), + <<"aa--">> = iolist_to_binary(join(re:split("aa--","^(?:a?b?)*$",[]))), <<":a : :b: c">> = iolist_to_binary(join(re:split("a b -c","((?s)^a(.))((?m)^b$)",[trim]))), +c","((?s)^a(.))((?m)^b$)",[trim]))), <<":a : :b: c">> = iolist_to_binary(join(re:split("a b -c","((?s)^a(.))((?m)^b$)",[{parts,2}]))), +c","((?s)^a(.))((?m)^b$)",[{parts,2}]))), <<":a : :b: c">> = iolist_to_binary(join(re:split("a b -c","((?s)^a(.))((?m)^b$)",[]))), +c","((?s)^a(.))((?m)^b$)",[]))), <<"a :b: c">> = iolist_to_binary(join(re:split("a b -c","((?m)^b$)",[trim]))), +c","((?m)^b$)",[trim]))), <<"a :b: c">> = iolist_to_binary(join(re:split("a b -c","((?m)^b$)",[{parts,2}]))), +c","((?m)^b$)",[{parts,2}]))), <<"a :b: c">> = iolist_to_binary(join(re:split("a b -c","((?m)^b$)",[]))), +c","((?m)^b$)",[]))), <<"a ">> = iolist_to_binary(join(re:split("a -b","(?m)^b",[trim]))), +b","(?m)^b",[trim]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","(?m)^b",[{parts,2}]))), +b","(?m)^b",[{parts,2}]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","(?m)^b",[]))), +b","(?m)^b",[]))), <<"a :b">> = iolist_to_binary(join(re:split("a -b","(?m)^(b)",[trim]))), +b","(?m)^(b)",[trim]))), <<"a :b:">> = iolist_to_binary(join(re:split("a -b","(?m)^(b)",[{parts,2}]))), +b","(?m)^(b)",[{parts,2}]))), <<"a :b:">> = iolist_to_binary(join(re:split("a -b","(?m)^(b)",[]))), +b","(?m)^(b)",[]))), <<"a :b">> = iolist_to_binary(join(re:split("a -b","((?m)^b)",[trim]))), +b","((?m)^b)",[trim]))), <<"a :b:">> = iolist_to_binary(join(re:split("a -b","((?m)^b)",[{parts,2}]))), +b","((?m)^b)",[{parts,2}]))), <<"a :b:">> = iolist_to_binary(join(re:split("a -b","((?m)^b)",[]))), +b","((?m)^b)",[]))), <<"a:b">> = iolist_to_binary(join(re:split("a -b","\\n((?m)^b)",[trim]))), +b","\\n((?m)^b)",[trim]))), <<"a:b:">> = iolist_to_binary(join(re:split("a -b","\\n((?m)^b)",[{parts,2}]))), +b","\\n((?m)^b)",[{parts,2}]))), <<"a:b:">> = iolist_to_binary(join(re:split("a -b","\\n((?m)^b)",[]))), +b","\\n((?m)^b)",[]))), <<"a b: ">> = iolist_to_binary(join(re:split("a b -c","((?s).)c(?!.)",[trim]))), +c","((?s).)c(?!.)",[trim]))), <<"a b: :">> = iolist_to_binary(join(re:split("a b -c","((?s).)c(?!.)",[{parts,2}]))), +c","((?s).)c(?!.)",[{parts,2}]))), <<"a b: :">> = iolist_to_binary(join(re:split("a b -c","((?s).)c(?!.)",[]))), +c","((?s).)c(?!.)",[]))), <<"a b: ">> = iolist_to_binary(join(re:split("a b -c","((?s).)c(?!.)",[trim]))), +c","((?s).)c(?!.)",[trim]))), <<"a b: :">> = iolist_to_binary(join(re:split("a b -c","((?s).)c(?!.)",[{parts,2}]))), +c","((?s).)c(?!.)",[{parts,2}]))), <<"a b: :">> = iolist_to_binary(join(re:split("a b -c","((?s).)c(?!.)",[]))), +c","((?s).)c(?!.)",[]))), <<"a :b ">> = iolist_to_binary(join(re:split("a b -c","((?s)b.)c(?!.)",[trim]))), +c","((?s)b.)c(?!.)",[trim]))), <<"a :b :">> = iolist_to_binary(join(re:split("a b -c","((?s)b.)c(?!.)",[{parts,2}]))), +c","((?s)b.)c(?!.)",[{parts,2}]))), <<"a :b :">> = iolist_to_binary(join(re:split("a b -c","((?s)b.)c(?!.)",[]))), +c","((?s)b.)c(?!.)",[]))), <<"a :b ">> = iolist_to_binary(join(re:split("a b -c","((?s)b.)c(?!.)",[trim]))), +c","((?s)b.)c(?!.)",[trim]))), <<"a :b :">> = iolist_to_binary(join(re:split("a b -c","((?s)b.)c(?!.)",[{parts,2}]))), +c","((?s)b.)c(?!.)",[{parts,2}]))), <<"a :b :">> = iolist_to_binary(join(re:split("a b -c","((?s)b.)c(?!.)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","()^b",[trim]))), +c","((?s)b.)c(?!.)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","()^b",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","()^b",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","()^b",[]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","()^b",[]))), <<"a b c">> = iolist_to_binary(join(re:split("a b -c","()^b",[trim]))), +c","()^b",[trim]))), <<"a b c">> = iolist_to_binary(join(re:split("a b -c","()^b",[{parts,2}]))), +c","()^b",[{parts,2}]))), <<"a b c">> = iolist_to_binary(join(re:split("a b -c","()^b",[]))), +c","()^b",[]))), <<"a b c">> = iolist_to_binary(join(re:split("a b -c","()^b",[trim]))), +c","()^b",[trim]))), <<"a b c">> = iolist_to_binary(join(re:split("a b -c","()^b",[{parts,2}]))), +c","()^b",[{parts,2}]))), <<"a b c">> = iolist_to_binary(join(re:split("a b -c","()^b",[]))), +c","()^b",[]))), <<"a :b: c">> = iolist_to_binary(join(re:split("a b -c","((?m)^b)",[trim]))), +c","((?m)^b)",[trim]))), <<"a :b: c">> = iolist_to_binary(join(re:split("a b -c","((?m)^b)",[{parts,2}]))), +c","((?m)^b)",[{parts,2}]))), <<"a :b: c">> = iolist_to_binary(join(re:split("a b -c","((?m)^b)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(x)?(?(1)a|b)",[trim]))), +c","((?m)^b)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(x)?(?(1)a|b)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(x)?(?(1)a|b)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(x)?(?(1)a|b)",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(x)?(?(1)a|b)",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[]))), - <<"">> = iolist_to_binary(join(re:split("a","(x)?(?(1)b|a)",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","(x)?(?(1)a|b)",[]))), + <<"">> = iolist_to_binary(join(re:split("a","(x)?(?(1)b|a)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","(x)?(?(1)b|a)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","(x)?(?(1)b|a)",[]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","(x)?(?(1)b|a)",[]))), ok. run30() -> - <<"">> = iolist_to_binary(join(re:split("a","()?(?(1)b|a)",[trim]))), + <<"">> = iolist_to_binary(join(re:split("a","()?(?(1)b|a)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","()?(?(1)b|a)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","()?(?(1)b|a)",[]))), - <<"">> = iolist_to_binary(join(re:split("a","()?(?(1)a|b)",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","()?(?(1)b|a)",[]))), + <<"">> = iolist_to_binary(join(re:split("a","()?(?(1)a|b)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","()?(?(1)a|b)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","()?(?(1)a|b)",[]))), - <<":(:)">> = iolist_to_binary(join(re:split("(blah)","^(\\()?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","()?(?(1)a|b)",[]))), + <<":(:)">> = iolist_to_binary(join(re:split("(blah)","^(\\()?blah(?(1)(\\)))$",[trim]))), <<":(:):">> = iolist_to_binary(join(re:split("(blah)","^(\\()?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<":(:):">> = iolist_to_binary(join(re:split("(blah)","^(\\()?blah(?(1)(\\)))$",[]))), - <<"">> = iolist_to_binary(join(re:split("blah","^(\\()?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<":(:):">> = iolist_to_binary(join(re:split("(blah)","^(\\()?blah(?(1)(\\)))$",[]))), + <<"">> = iolist_to_binary(join(re:split("blah","^(\\()?blah(?(1)(\\)))$",[trim]))), <<":::">> = iolist_to_binary(join(re:split("blah","^(\\()?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("blah","^(\\()?blah(?(1)(\\)))$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\()?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("blah","^(\\()?blah(?(1)(\\)))$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\()?blah(?(1)(\\)))$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\()?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\()?blah(?(1)(\\)))$",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(\\()?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\()?blah(?(1)(\\)))$",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(\\()?blah(?(1)(\\)))$",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","^(\\()?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(\\()?blah(?(1)(\\)))$",[]))), - <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\()?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(\\()?blah(?(1)(\\)))$",[]))), + <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\()?blah(?(1)(\\)))$",[trim]))), <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\()?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\()?blah(?(1)(\\)))$",[]))), - <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\()?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\()?blah(?(1)(\\)))$",[]))), + <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\()?blah(?(1)(\\)))$",[trim]))), <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\()?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\()?blah(?(1)(\\)))$",[]))), - <<":(:)">> = iolist_to_binary(join(re:split("(blah)","^(\\(+)?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\()?blah(?(1)(\\)))$",[]))), + <<":(:)">> = iolist_to_binary(join(re:split("(blah)","^(\\(+)?blah(?(1)(\\)))$",[trim]))), <<":(:):">> = iolist_to_binary(join(re:split("(blah)","^(\\(+)?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<":(:):">> = iolist_to_binary(join(re:split("(blah)","^(\\(+)?blah(?(1)(\\)))$",[]))), - <<"">> = iolist_to_binary(join(re:split("blah","^(\\(+)?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<":(:):">> = iolist_to_binary(join(re:split("(blah)","^(\\(+)?blah(?(1)(\\)))$",[]))), + <<"">> = iolist_to_binary(join(re:split("blah","^(\\(+)?blah(?(1)(\\)))$",[trim]))), <<":::">> = iolist_to_binary(join(re:split("blah","^(\\(+)?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("blah","^(\\(+)?blah(?(1)(\\)))$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\(+)?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("blah","^(\\(+)?blah(?(1)(\\)))$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\(+)?blah(?(1)(\\)))$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\(+)?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\(+)?blah(?(1)(\\)))$",[]))), - <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\(+)?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\(+)?blah(?(1)(\\)))$",[]))), + <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\(+)?blah(?(1)(\\)))$",[trim]))), <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\(+)?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\(+)?blah(?(1)(\\)))$",[]))), - <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\(+)?blah(?(1)(\\)))$",[trim]))), + 2}]))), + <<"blah)">> = iolist_to_binary(join(re:split("blah)","^(\\(+)?blah(?(1)(\\)))$",[]))), + <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\(+)?blah(?(1)(\\)))$",[trim]))), <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\(+)?blah(?(1)(\\)))$",[{parts, - 2}]))), - <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\(+)?blah(?(1)(\\)))$",[]))), - <<"">> = iolist_to_binary(join(re:split("a","(?(?!a)b|a)",[trim]))), + 2}]))), + <<"(blah">> = iolist_to_binary(join(re:split("(blah","^(\\(+)?blah(?(1)(\\)))$",[]))), + <<"">> = iolist_to_binary(join(re:split("a","(?(?!a)b|a)",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","(?(?!a)b|a)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","(?(?!a)b|a)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=a)b|a)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","(?(?!a)b|a)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=a)b|a)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=a)b|a)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=a)b|a)",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?(?=a)b|a)",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[]))), - <<"">> = iolist_to_binary(join(re:split("a","(?(?=a)a|b)",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","(?(?=a)b|a)",[]))), + <<"">> = iolist_to_binary(join(re:split("a","(?(?=a)a|b)",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","(?(?=a)a|b)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","(?(?=a)a|b)",[]))), - <<"a:a:aab">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","(?(?=a)a|b)",[]))), + <<"a:a:aab">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[trim]))), <<"a:a:aab:">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[{parts, - 2}]))), - <<"a:a:aab:">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[]))), - <<":one:">> = iolist_to_binary(join(re:split("one:","(\\w+:)+",[trim]))), + 2}]))), + <<"a:a:aab:">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[]))), + <<":one:">> = iolist_to_binary(join(re:split("one:","(\\w+:)+",[trim]))), <<":one::">> = iolist_to_binary(join(re:split("one:","(\\w+:)+",[{parts, - 2}]))), - <<":one::">> = iolist_to_binary(join(re:split("one:","(\\w+:)+",[]))), - <<"a:a">> = iolist_to_binary(join(re:split("a","$(?<=^(a))",[trim]))), + 2}]))), + <<":one::">> = iolist_to_binary(join(re:split("one:","(\\w+:)+",[]))), + <<"a:a">> = iolist_to_binary(join(re:split("a","$(?<=^(a))",[trim]))), <<"a:a:">> = iolist_to_binary(join(re:split("a","$(?<=^(a))",[{parts, - 2}]))), - <<"a:a:">> = iolist_to_binary(join(re:split("a","$(?<=^(a))",[]))), - <<"a:a:aab">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[trim]))), + 2}]))), + <<"a:a:">> = iolist_to_binary(join(re:split("a","$(?<=^(a))",[]))), + <<"a:a:aab">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[trim]))), <<"a:a:aab:">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[{parts, - 2}]))), - <<"a:a:aab:">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?=(a+?))\\1ab",[trim]))), + 2}]))), + <<"a:a:aab:">> = iolist_to_binary(join(re:split("aaab","(?=(a+?))(\\1ab)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?=(a+?))\\1ab",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?=(a+?))\\1ab",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?=(a+?))\\1ab",[]))), - <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?=(a+?))\\1ab",[]))), + <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[trim]))), <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[{parts, - 2}]))), - <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[]))), - <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[trim]))), + 2}]))), + <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[]))), + <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[trim]))), <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[{parts, - 2}]))), - <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[]))), - <<"::abcd">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<"aaab">> = iolist_to_binary(join(re:split("aaab","^(?=(a+?))\\1ab",[]))), + <<"::abcd">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[trim]))), <<"::abcd:">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<"::abcd:">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[]))), - <<":xy:z::::abcd">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<"::abcd:">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[]))), + <<":xy:z::::abcd">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[trim]))), <<":xy:z::::abcd:">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<":xy:z::::abcd:">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[]))), - <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[trim]))), + 2}]))), + <<":xy:z::::abcd:">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[]))), + <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[trim]))), <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[{parts, - 2}]))), - <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[]))), - <<"c:aa">> = iolist_to_binary(join(re:split("caab","(a*)b+",[trim]))), + 2}]))), + <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[]))), + <<"c:aa">> = iolist_to_binary(join(re:split("caab","(a*)b+",[trim]))), <<"c:aa:">> = iolist_to_binary(join(re:split("caab","(a*)b+",[{parts, - 2}]))), - <<"c:aa:">> = iolist_to_binary(join(re:split("caab","(a*)b+",[]))), - <<"::abcd">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<"c:aa:">> = iolist_to_binary(join(re:split("caab","(a*)b+",[]))), + <<"::abcd">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[trim]))), <<"::abcd:">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<"::abcd:">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[]))), - <<":xy:z::::abcd">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<"::abcd:">> = iolist_to_binary(join(re:split("abcd","([\\w:]+::)?(\\w+)$",[]))), + <<":xy:z::::abcd">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[trim]))), <<":xy:z::::abcd:">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<":xy:z::::abcd:">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[]))), - <<"*** ::Failers">> = iolist_to_binary(join(re:split("*** Failers","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<":xy:z::::abcd:">> = iolist_to_binary(join(re:split("xy:z:::abcd","([\\w:]+::)?(\\w+)$",[]))), + <<"*** ::Failers">> = iolist_to_binary(join(re:split("*** Failers","([\\w:]+::)?(\\w+)$",[trim]))), <<"*** ::Failers:">> = iolist_to_binary(join(re:split("*** Failers","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<"*** ::Failers:">> = iolist_to_binary(join(re:split("*** Failers","([\\w:]+::)?(\\w+)$",[]))), - <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<"*** ::Failers:">> = iolist_to_binary(join(re:split("*** Failers","([\\w:]+::)?(\\w+)$",[]))), + <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[trim]))), <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[]))), - <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[trim]))), + 2}]))), + <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[]))), + <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[trim]))), <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[{parts, - 2}]))), - <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[]))), - <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[trim]))), + 2}]))), + <<"abcd:">> = iolist_to_binary(join(re:split("abcd:","([\\w:]+::)?(\\w+)$",[]))), + <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[trim]))), <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[{parts, - 2}]))), - <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[]))), + 2}]))), + <<":c:d">> = iolist_to_binary(join(re:split("aexycd","^[^bcd]*(c+)",[]))), ok. run31() -> - <<"">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[trim]))), + <<"">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[]))), - <<"a::[:b]::">> = iolist_to_binary(join(re:split("a:[b]:","([[:]+)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaab","(?>a+)b",[]))), + <<"a::[:b]::">> = iolist_to_binary(join(re:split("a:[b]:","([[:]+)",[trim]))), <<"a::[:b]:">> = iolist_to_binary(join(re:split("a:[b]:","([[:]+)",[{parts, - 2}]))), - <<"a::[:b]:::">> = iolist_to_binary(join(re:split("a:[b]:","([[:]+)",[]))), - <<"a:=[:b]:=">> = iolist_to_binary(join(re:split("a=[b]=","([[=]+)",[trim]))), + 2}]))), + <<"a::[:b]:::">> = iolist_to_binary(join(re:split("a:[b]:","([[:]+)",[]))), + <<"a:=[:b]:=">> = iolist_to_binary(join(re:split("a=[b]=","([[=]+)",[trim]))), <<"a:=[:b]=">> = iolist_to_binary(join(re:split("a=[b]=","([[=]+)",[{parts, - 2}]))), - <<"a:=[:b]:=:">> = iolist_to_binary(join(re:split("a=[b]=","([[=]+)",[]))), - <<"a:.[:b]:.">> = iolist_to_binary(join(re:split("a.[b].","([[.]+)",[trim]))), + 2}]))), + <<"a:=[:b]:=:">> = iolist_to_binary(join(re:split("a=[b]=","([[=]+)",[]))), + <<"a:.[:b]:.">> = iolist_to_binary(join(re:split("a.[b].","([[.]+)",[trim]))), <<"a:.[:b].">> = iolist_to_binary(join(re:split("a.[b].","([[.]+)",[{parts, - 2}]))), - <<"a:.[:b]:.:">> = iolist_to_binary(join(re:split("a.[b].","([[.]+)",[]))), - <<":aaab">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[trim]))), + 2}]))), + <<"a:.[:b]:.:">> = iolist_to_binary(join(re:split("a.[b].","([[.]+)",[]))), + <<":aaab">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[trim]))), <<":aaab:">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[{parts, - 2}]))), - <<":aaab:">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[]))), - <<":aaa">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[trim]))), + 2}]))), + <<":aaab:">> = iolist_to_binary(join(re:split("aaab","((?>a+)b)",[]))), + <<":aaa">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[trim]))), <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[{parts, - 2}]))), - <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[]))), - <<"((:x">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[trim]))), + 2}]))), + <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(?>(a+))b",[]))), + <<"((:x">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[trim]))), <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[{parts, - 2}]))), - <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Z",[trim]))), + 2}]))), + <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","((?>[^()]+)|\\([^()]*\\))+",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Z",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Z",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Z",[]))), - <<"aaab">> = iolist_to_binary(join(re:split("aaab","a\\Z",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a\\Z",[]))), + <<"aaab">> = iolist_to_binary(join(re:split("aaab","a\\Z",[trim]))), <<"aaab">> = iolist_to_binary(join(re:split("aaab","a\\Z",[{parts, - 2}]))), - <<"aaab">> = iolist_to_binary(join(re:split("aaab","a\\Z",[]))), + 2}]))), + <<"aaab">> = iolist_to_binary(join(re:split("aaab","a\\Z",[]))), <<"a b">> = iolist_to_binary(join(re:split("a -b","a\\Z",[trim]))), +b","a\\Z",[trim]))), <<"a b">> = iolist_to_binary(join(re:split("a -b","a\\Z",[{parts,2}]))), +b","a\\Z",[{parts,2}]))), <<"a b">> = iolist_to_binary(join(re:split("a -b","a\\Z",[]))), +b","a\\Z",[]))), <<"a ">> = iolist_to_binary(join(re:split("a -b","b\\Z",[trim]))), +b","b\\Z",[trim]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","b\\Z",[{parts,2}]))), +b","b\\Z",[{parts,2}]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","b\\Z",[]))), +b","b\\Z",[]))), <<"a ">> = iolist_to_binary(join(re:split("a -b","b\\Z",[trim]))), +b","b\\Z",[trim]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","b\\Z",[{parts,2}]))), +b","b\\Z",[{parts,2}]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","b\\Z",[]))), +b","b\\Z",[]))), <<"a ">> = iolist_to_binary(join(re:split("a -b","b\\z",[trim]))), +b","b\\z",[trim]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","b\\z",[{parts,2}]))), +b","b\\z",[{parts,2}]))), <<"a :">> = iolist_to_binary(join(re:split("a -b","b\\z",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","b\\z",[trim]))), +b","b\\z",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","b\\z",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","b\\z",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","b\\z",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","b\\z",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("abc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("abc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("a-b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("abc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("a-b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a-b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a-b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("0-9","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a-b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("0-9","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("0-9","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("0-9","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("a.b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("0-9","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("a.b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a.b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a.b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("5.6.7","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a.b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("5.6.7","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("5.6.7","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("5.6.7","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("the.quick.brown.fox","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("5.6.7","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("the.quick.brown.fox","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("the.quick.brown.fox","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("the.quick.brown.fox","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("a100.b200.300c","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("the.quick.brown.fox","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("a100.b200.300c","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a100.b200.300c","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a100.b200.300c","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("12-ab.1245","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a100.b200.300c","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("12-ab.1245","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("12-ab.1245","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("12-ab.1245","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("12-ab.1245","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"">> = iolist_to_binary(join(re:split("","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"">> = iolist_to_binary(join(re:split("","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"">> = iolist_to_binary(join(re:split("","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"">> = iolist_to_binary(join(re:split("","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<".a">> = iolist_to_binary(join(re:split(".a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"">> = iolist_to_binary(join(re:split("","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<".a">> = iolist_to_binary(join(re:split(".a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<".a">> = iolist_to_binary(join(re:split(".a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<".a">> = iolist_to_binary(join(re:split(".a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"-a">> = iolist_to_binary(join(re:split("-a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<".a">> = iolist_to_binary(join(re:split(".a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"-a">> = iolist_to_binary(join(re:split("-a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"-a">> = iolist_to_binary(join(re:split("-a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"-a">> = iolist_to_binary(join(re:split("-a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"a-">> = iolist_to_binary(join(re:split("a-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"-a">> = iolist_to_binary(join(re:split("-a","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"a-">> = iolist_to_binary(join(re:split("a-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"a-">> = iolist_to_binary(join(re:split("a-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"a-">> = iolist_to_binary(join(re:split("a-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"a.">> = iolist_to_binary(join(re:split("a.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"a-">> = iolist_to_binary(join(re:split("a-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"a.">> = iolist_to_binary(join(re:split("a.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"a.">> = iolist_to_binary(join(re:split("a.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"a.">> = iolist_to_binary(join(re:split("a.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"a_b">> = iolist_to_binary(join(re:split("a_b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"a.">> = iolist_to_binary(join(re:split("a.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"a_b">> = iolist_to_binary(join(re:split("a_b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"a_b">> = iolist_to_binary(join(re:split("a_b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"a_b">> = iolist_to_binary(join(re:split("a_b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"a.-">> = iolist_to_binary(join(re:split("a.-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"a_b">> = iolist_to_binary(join(re:split("a_b","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"a.-">> = iolist_to_binary(join(re:split("a.-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"a.-">> = iolist_to_binary(join(re:split("a.-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"a.-">> = iolist_to_binary(join(re:split("a.-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"a..">> = iolist_to_binary(join(re:split("a..","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"a.-">> = iolist_to_binary(join(re:split("a.-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"a..">> = iolist_to_binary(join(re:split("a..","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"a..">> = iolist_to_binary(join(re:split("a..","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"a..">> = iolist_to_binary(join(re:split("a..","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"ab..bc">> = iolist_to_binary(join(re:split("ab..bc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"a..">> = iolist_to_binary(join(re:split("a..","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"ab..bc">> = iolist_to_binary(join(re:split("ab..bc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"ab..bc">> = iolist_to_binary(join(re:split("ab..bc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"ab..bc">> = iolist_to_binary(join(re:split("ab..bc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"the.quick.brown.fox-">> = iolist_to_binary(join(re:split("the.quick.brown.fox-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"ab..bc">> = iolist_to_binary(join(re:split("ab..bc","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"the.quick.brown.fox-">> = iolist_to_binary(join(re:split("the.quick.brown.fox-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"the.quick.brown.fox-">> = iolist_to_binary(join(re:split("the.quick.brown.fox-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"the.quick.brown.fox-">> = iolist_to_binary(join(re:split("the.quick.brown.fox-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"the.quick.brown.fox.">> = iolist_to_binary(join(re:split("the.quick.brown.fox.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"the.quick.brown.fox-">> = iolist_to_binary(join(re:split("the.quick.brown.fox-","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"the.quick.brown.fox.">> = iolist_to_binary(join(re:split("the.quick.brown.fox.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"the.quick.brown.fox.">> = iolist_to_binary(join(re:split("the.quick.brown.fox.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"the.quick.brown.fox.">> = iolist_to_binary(join(re:split("the.quick.brown.fox.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"the.quick.brown.fox_">> = iolist_to_binary(join(re:split("the.quick.brown.fox_","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"the.quick.brown.fox.">> = iolist_to_binary(join(re:split("the.quick.brown.fox.","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"the.quick.brown.fox_">> = iolist_to_binary(join(re:split("the.quick.brown.fox_","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"the.quick.brown.fox_">> = iolist_to_binary(join(re:split("the.quick.brown.fox_","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"the.quick.brown.fox_">> = iolist_to_binary(join(re:split("the.quick.brown.fox_","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<"the.quick.brown.fox+">> = iolist_to_binary(join(re:split("the.quick.brown.fox+","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), + 2}]))), + <<"the.quick.brown.fox_">> = iolist_to_binary(join(re:split("the.quick.brown.fox_","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<"the.quick.brown.fox+">> = iolist_to_binary(join(re:split("the.quick.brown.fox+","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[trim]))), <<"the.quick.brown.fox+">> = iolist_to_binary(join(re:split("the.quick.brown.fox+","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[{parts, - 2}]))), - <<"the.quick.brown.fox+">> = iolist_to_binary(join(re:split("the.quick.brown.fox+","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), - <<":abcd">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd|wxyz))",[trim]))), + 2}]))), + <<"the.quick.brown.fox+">> = iolist_to_binary(join(re:split("the.quick.brown.fox+","^(?>(?(1)\\.|())[^\\W_](?>[a-z0-9-]*[^\\W_])?)+$",[]))), + <<":abcd">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd|wxyz))",[trim]))), <<":abcd:">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd|wxyz))",[{parts, - 2}]))), - <<":abcd:">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd|wxyz))",[]))), - <<":wxyz">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd|wxyz))",[trim]))), + 2}]))), + <<":abcd:">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd|wxyz))",[]))), + <<":wxyz">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd|wxyz))",[trim]))), <<":wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd|wxyz))",[{parts, - 2}]))), - <<":wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd|wxyz))",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>.*)(?<=(abcd|wxyz))",[trim]))), + 2}]))), + <<":wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd|wxyz))",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>.*)(?<=(abcd|wxyz))",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>.*)(?<=(abcd|wxyz))",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>.*)(?<=(abcd|wxyz))",[]))), - <<"a rather long string that doesn't end with one of them">> = iolist_to_binary(join(re:split("a rather long string that doesn't end with one of them","(?>.*)(?<=(abcd|wxyz))",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?>.*)(?<=(abcd|wxyz))",[]))), + <<"a rather long string that doesn't end with one of them">> = iolist_to_binary(join(re:split("a rather long string that doesn't end with one of them","(?>.*)(?<=(abcd|wxyz))",[trim]))), <<"a rather long string that doesn't end with one of them">> = iolist_to_binary(join(re:split("a rather long string that doesn't end with one of them","(?>.*)(?<=(abcd|wxyz))",[{parts, - 2}]))), - <<"a rather long string that doesn't end with one of them">> = iolist_to_binary(join(re:split("a rather long string that doesn't end with one of them","(?>.*)(?<=(abcd|wxyz))",[]))), - <<"">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[trim]))), + 2}]))), + <<"a rather long string that doesn't end with one of them">> = iolist_to_binary(join(re:split("a rather long string that doesn't end with one of them","(?>.*)(?<=(abcd|wxyz))",[]))), + <<"">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[trim]))), <<":">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark otherword","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[trim]))), <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[{parts, - 2}]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?>[a-zA-Z0-9]+ ){0,30}otherword",[trim]))), + 2}]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark","word (?>(?:(?!otherword)[a-zA-Z0-9]+ ){0,30})otherword",[]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?>[a-zA-Z0-9]+ ){0,30}otherword",[trim]))), <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?>[a-zA-Z0-9]+ ){0,30}otherword",[{parts, - 2}]))), - <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?>[a-zA-Z0-9]+ ){0,30}otherword",[]))), - <<"999">> = iolist_to_binary(join(re:split("999foo","(?<=\\d{3}(?!999))foo",[trim]))), + 2}]))), + <<"word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope">> = iolist_to_binary(join(re:split("word cat dog elephant mussel cow horse canary baboon snake shark the quick brown fox and the lazy dog and several other words getting close to thirty by now I hope","word (?>[a-zA-Z0-9]+ ){0,30}otherword",[]))), + <<"999">> = iolist_to_binary(join(re:split("999foo","(?<=\\d{3}(?!999))foo",[trim]))), <<"999:">> = iolist_to_binary(join(re:split("999foo","(?<=\\d{3}(?!999))foo",[{parts, - 2}]))), - <<"999:">> = iolist_to_binary(join(re:split("999foo","(?<=\\d{3}(?!999))foo",[]))), - <<"123999">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999))foo",[trim]))), + 2}]))), + <<"999:">> = iolist_to_binary(join(re:split("999foo","(?<=\\d{3}(?!999))foo",[]))), + <<"123999">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999))foo",[trim]))), <<"123999:">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999))foo",[{parts, - 2}]))), - <<"123999:">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999))foo",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999))foo",[trim]))), + 2}]))), + <<"123999:">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999))foo",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999))foo",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999))foo",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999))foo",[]))), - <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999))foo",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999))foo",[]))), + <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999))foo",[trim]))), <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999))foo",[{parts, - 2}]))), - <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999))foo",[]))), - <<"999">> = iolist_to_binary(join(re:split("999foo","(?<=(?!...999)\\d{3})foo",[trim]))), + 2}]))), + <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999))foo",[]))), + <<"999">> = iolist_to_binary(join(re:split("999foo","(?<=(?!...999)\\d{3})foo",[trim]))), <<"999:">> = iolist_to_binary(join(re:split("999foo","(?<=(?!...999)\\d{3})foo",[{parts, - 2}]))), - <<"999:">> = iolist_to_binary(join(re:split("999foo","(?<=(?!...999)\\d{3})foo",[]))), - <<"123999">> = iolist_to_binary(join(re:split("123999foo","(?<=(?!...999)\\d{3})foo",[trim]))), + 2}]))), + <<"999:">> = iolist_to_binary(join(re:split("999foo","(?<=(?!...999)\\d{3})foo",[]))), + <<"123999">> = iolist_to_binary(join(re:split("123999foo","(?<=(?!...999)\\d{3})foo",[trim]))), <<"123999:">> = iolist_to_binary(join(re:split("123999foo","(?<=(?!...999)\\d{3})foo",[{parts, - 2}]))), - <<"123999:">> = iolist_to_binary(join(re:split("123999foo","(?<=(?!...999)\\d{3})foo",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?!...999)\\d{3})foo",[trim]))), + 2}]))), + <<"123999:">> = iolist_to_binary(join(re:split("123999foo","(?<=(?!...999)\\d{3})foo",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?!...999)\\d{3})foo",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?!...999)\\d{3})foo",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?!...999)\\d{3})foo",[]))), - <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=(?!...999)\\d{3})foo",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=(?!...999)\\d{3})foo",[]))), + <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=(?!...999)\\d{3})foo",[trim]))), <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=(?!...999)\\d{3})foo",[{parts, - 2}]))), - <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=(?!...999)\\d{3})foo",[]))), - <<"123abc">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999)...)foo",[trim]))), + 2}]))), + <<"123abcfoo">> = iolist_to_binary(join(re:split("123abcfoo","(?<=(?!...999)\\d{3})foo",[]))), + <<"123abc">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999)...)foo",[trim]))), <<"123abc:">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999)...)foo",[{parts, - 2}]))), - <<"123abc:">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999)...)foo",[]))), - <<"123456">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}(?!999)...)foo",[trim]))), + 2}]))), + <<"123abc:">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}(?!999)...)foo",[]))), + <<"123456">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}(?!999)...)foo",[trim]))), <<"123456:">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}(?!999)...)foo",[{parts, - 2}]))), - <<"123456:">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}(?!999)...)foo",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999)...)foo",[trim]))), + 2}]))), + <<"123456:">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}(?!999)...)foo",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999)...)foo",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999)...)foo",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999)...)foo",[]))), - <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999)...)foo",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}(?!999)...)foo",[]))), + <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999)...)foo",[trim]))), <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999)...)foo",[{parts, - 2}]))), - <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999)...)foo",[]))), + 2}]))), + <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}(?!999)...)foo",[]))), ok. run32() -> - <<"123abc">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}...)(?<!999)foo",[trim]))), + <<"123abc">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}...)(?<!999)foo",[trim]))), <<"123abc:">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}...)(?<!999)foo",[{parts, - 2}]))), - <<"123abc:">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}...)(?<!999)foo",[]))), - <<"123456">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}...)(?<!999)foo",[trim]))), + 2}]))), + <<"123abc:">> = iolist_to_binary(join(re:split("123abcfoo","(?<=\\d{3}...)(?<!999)foo",[]))), + <<"123456">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}...)(?<!999)foo",[trim]))), <<"123456:">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}...)(?<!999)foo",[{parts, - 2}]))), - <<"123456:">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}...)(?<!999)foo",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}...)(?<!999)foo",[trim]))), + 2}]))), + <<"123456:">> = iolist_to_binary(join(re:split("123456foo","(?<=\\d{3}...)(?<!999)foo",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}...)(?<!999)foo",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}...)(?<!999)foo",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}...)(?<!999)foo",[]))), - <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}...)(?<!999)foo",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?<=\\d{3}...)(?<!999)foo",[]))), + <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}...)(?<!999)foo",[trim]))), <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}...)(?<!999)foo",[{parts, - 2}]))), - <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}...)(?<!999)foo",[]))), + 2}]))), + <<"123999foo">> = iolist_to_binary(join(re:split("123999foo","(?<=\\d{3}...)(?<!999)foo",[]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching @@ -28108,20 +28108,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching @@ -28129,20 +28129,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href='abcd xyz pqr' cats","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href='abcd xyz pqr' cats","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching @@ -28150,20 +28150,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href='abcd xyz pqr' cats","<a[\\s]+href[\\s]*=[\\s]* # find <a href= ([\\\"\\'])? # find single or double quote (?(1) (.*?)\\1 | ([^\\s]+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching @@ -28171,20 +28171,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching @@ -28192,20 +28192,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href = 'abcd xyz pqr' cats","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href = 'abcd xyz pqr' cats","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching @@ -28213,20 +28213,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href = 'abcd xyz pqr' cats","<a\\s+href\\s*=\\s* # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching @@ -28234,20 +28234,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":::abcd: xyz">> = iolist_to_binary(join(re:split("<a href=abcd xyz","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching @@ -28255,20 +28255,20 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":\":abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href=\"abcd xyz pqr\" cats","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), + extended]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href = 'abcd xyz pqr' cats","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, extended, - trim]))), + trim]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href = 'abcd xyz pqr' cats","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching @@ -28276,303 +28276,303 @@ run32() -> dotall, extended, {parts, - 2}]))), + 2}]))), <<":':abcd xyz pqr:: cats">> = iolist_to_binary(join(re:split("<a href = 'abcd xyz pqr' cats","<a\\s+href(?>\\s*)=(?>\\s*) # find <a href= ([\"'])? # find single or double quote (?(1) (.*?)\\1 | (\\S+)) # if quote found, match up to next matching # quote, otherwise match up to next space",[caseless, dotall, - extended]))), - <<":A:Z:B:::C:::D:::E:::F:::G">> = iolist_to_binary(join(re:split("ZABCDEFG","((Z)+|A)*",[trim]))), + extended]))), + <<":A:Z:B:::C:::D:::E:::F:::G">> = iolist_to_binary(join(re:split("ZABCDEFG","((Z)+|A)*",[trim]))), <<":A:Z:BCDEFG">> = iolist_to_binary(join(re:split("ZABCDEFG","((Z)+|A)*",[{parts, - 2}]))), - <<":A:Z:B:::C:::D:::E:::F:::G:::">> = iolist_to_binary(join(re:split("ZABCDEFG","((Z)+|A)*",[]))), - <<":A::B:::C:::D:::E:::F:::G">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z()|A)*",[trim]))), + 2}]))), + <<":A:Z:B:::C:::D:::E:::F:::G:::">> = iolist_to_binary(join(re:split("ZABCDEFG","((Z)+|A)*",[]))), + <<":A::B:::C:::D:::E:::F:::G">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z()|A)*",[trim]))), <<":A::BCDEFG">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z()|A)*",[{parts, - 2}]))), - <<":A::B:::C:::D:::E:::F:::G:::">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z()|A)*",[]))), - <<":A:::B::::C::::D::::E::::F::::G">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z(())|A)*",[trim]))), + 2}]))), + <<":A::B:::C:::D:::E:::F:::G:::">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z()|A)*",[]))), + <<":A:::B::::C::::D::::E::::F::::G">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z(())|A)*",[trim]))), <<":A:::BCDEFG">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z(())|A)*",[{parts, - 2}]))), - <<":A:::B::::C::::D::::E::::F::::G::::">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z(())|A)*",[]))), - <<":A:B::C::D::E::F::G">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>Z)+|A)*",[trim]))), + 2}]))), + <<":A:::B::::C::::D::::E::::F::::G::::">> = iolist_to_binary(join(re:split("ZABCDEFG","(Z(())|A)*",[]))), + <<":A:B::C::D::E::F::G">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>Z)+|A)*",[trim]))), <<":A:BCDEFG">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>Z)+|A)*",[{parts, - 2}]))), - <<":A:B::C::D::E::F::G::">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>Z)+|A)*",[]))), - <<"Z::::B::C::D::E::F::G">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>)+|A)*",[trim]))), + 2}]))), + <<":A:B::C::D::E::F::G::">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>Z)+|A)*",[]))), + <<"Z::::B::C::D::E::F::G">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>)+|A)*",[trim]))), <<"Z::ABCDEFG">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>)+|A)*",[{parts, - 2}]))), - <<"Z::::B::C::D::E::F::G::">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>)+|A)*",[]))), - <<":b:b:b">> = iolist_to_binary(join(re:split("abbab","a*",[trim]))), + 2}]))), + <<"Z::::B::C::D::E::F::G::">> = iolist_to_binary(join(re:split("ZABCDEFG","((?>)+|A)*",[]))), + <<":b:b:b">> = iolist_to_binary(join(re:split("abbab","a*",[trim]))), <<":bbab">> = iolist_to_binary(join(re:split("abbab","a*",[{parts, - 2}]))), - <<":b:b:b:">> = iolist_to_binary(join(re:split("abbab","a*",[]))), - <<":bcde">> = iolist_to_binary(join(re:split("abcde","^[\\d-a]",[trim]))), + 2}]))), + <<":b:b:b:">> = iolist_to_binary(join(re:split("abbab","a*",[]))), + <<":bcde">> = iolist_to_binary(join(re:split("abcde","^[\\d-a]",[trim]))), <<":bcde">> = iolist_to_binary(join(re:split("abcde","^[\\d-a]",[{parts, - 2}]))), - <<":bcde">> = iolist_to_binary(join(re:split("abcde","^[\\d-a]",[]))), - <<":things">> = iolist_to_binary(join(re:split("-things","^[\\d-a]",[trim]))), + 2}]))), + <<":bcde">> = iolist_to_binary(join(re:split("abcde","^[\\d-a]",[]))), + <<":things">> = iolist_to_binary(join(re:split("-things","^[\\d-a]",[trim]))), <<":things">> = iolist_to_binary(join(re:split("-things","^[\\d-a]",[{parts, - 2}]))), - <<":things">> = iolist_to_binary(join(re:split("-things","^[\\d-a]",[]))), - <<":digit">> = iolist_to_binary(join(re:split("0digit","^[\\d-a]",[trim]))), + 2}]))), + <<":things">> = iolist_to_binary(join(re:split("-things","^[\\d-a]",[]))), + <<":digit">> = iolist_to_binary(join(re:split("0digit","^[\\d-a]",[trim]))), <<":digit">> = iolist_to_binary(join(re:split("0digit","^[\\d-a]",[{parts, - 2}]))), - <<":digit">> = iolist_to_binary(join(re:split("0digit","^[\\d-a]",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[\\d-a]",[trim]))), + 2}]))), + <<":digit">> = iolist_to_binary(join(re:split("0digit","^[\\d-a]",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[\\d-a]",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[\\d-a]",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[\\d-a]",[]))), - <<"bcdef">> = iolist_to_binary(join(re:split("bcdef","^[\\d-a]",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^[\\d-a]",[]))), + <<"bcdef">> = iolist_to_binary(join(re:split("bcdef","^[\\d-a]",[trim]))), <<"bcdef">> = iolist_to_binary(join(re:split("bcdef","^[\\d-a]",[{parts, - 2}]))), - <<"bcdef">> = iolist_to_binary(join(re:split("bcdef","^[\\d-a]",[]))), + 2}]))), + <<"bcdef">> = iolist_to_binary(join(re:split("bcdef","^[\\d-a]",[]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","[[:space:]]+",[trim]))), +
<","[[:space:]]+",[trim]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","[[:space:]]+",[{parts,2}]))), +
<","[[:space:]]+",[{parts,2}]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","[[:space:]]+",[]))), +
<","[[:space:]]+",[]))), <<">:
<">> = iolist_to_binary(join(re:split("> -
<","[[:blank:]]+",[trim]))), +
<","[[:blank:]]+",[trim]))), <<">:
<">> = iolist_to_binary(join(re:split("> -
<","[[:blank:]]+",[{parts,2}]))), +
<","[[:blank:]]+",[{parts,2}]))), <<">:
<">> = iolist_to_binary(join(re:split("> -
<","[[:blank:]]+",[]))), +
<","[[:blank:]]+",[]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","[\\s]+",[trim]))), +
<","[\\s]+",[trim]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","[\\s]+",[{parts,2}]))), +
<","[\\s]+",[{parts,2}]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","[\\s]+",[]))), +
<","[\\s]+",[]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","\\s+",[trim]))), +
<","\\s+",[trim]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","\\s+",[{parts,2}]))), +
<","\\s+",[{parts,2}]))), <<">:<">> = iolist_to_binary(join(re:split("> -
<","\\s+",[]))), +
<","\\s+",[]))), <<"">> = iolist_to_binary(join(re:split("ab","ab",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("ab","ab",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","ab",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","ab",[extended]))), <<"a :b">> = iolist_to_binary(join(re:split("a -xb","(?!\\A)x",[multiline,trim]))), +xb","(?!\\A)x",[multiline,trim]))), <<"a :b">> = iolist_to_binary(join(re:split("a -xb","(?!\\A)x",[multiline,{parts,2}]))), +xb","(?!\\A)x",[multiline,{parts,2}]))), <<"a :b">> = iolist_to_binary(join(re:split("a -xb","(?!\\A)x",[multiline]))), +xb","(?!\\A)x",[multiline]))), <<"a xb">> = iolist_to_binary(join(re:split("a -xb","(?!^)x",[multiline,trim]))), +xb","(?!^)x",[multiline,trim]))), <<"a xb">> = iolist_to_binary(join(re:split("a -xb","(?!^)x",[multiline,{parts,2}]))), +xb","(?!^)x",[multiline,{parts,2}]))), <<"a xb">> = iolist_to_binary(join(re:split("a -xb","(?!^)x",[multiline]))), - <<"">> = iolist_to_binary(join(re:split("abcabcabc","abc\\Qabc\\Eabc",[trim]))), +xb","(?!^)x",[multiline]))), + <<"">> = iolist_to_binary(join(re:split("abcabcabc","abc\\Qabc\\Eabc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcabcabc","abc\\Qabc\\Eabc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcabcabc","abc\\Qabc\\Eabc",[]))), - <<"">> = iolist_to_binary(join(re:split("abc(*+|abc","abc\\Q(*+|\\Eabc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcabcabc","abc\\Qabc\\Eabc",[]))), + <<"">> = iolist_to_binary(join(re:split("abc(*+|abc","abc\\Q(*+|\\Eabc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc(*+|abc","abc\\Q(*+|\\Eabc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc(*+|abc","abc\\Q(*+|\\Eabc",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc(*+|abc","abc\\Q(*+|\\Eabc",[]))), ok. run33() -> <<"">> = iolist_to_binary(join(re:split("abc abcabc"," abc\\Q abc\\Eabc",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("abc abcabc"," abc\\Q abc\\Eabc",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc abcabc"," abc\\Q abc\\Eabc",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc abcabc"," abc\\Q abc\\Eabc",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," abc\\Q abc\\Eabc",[extended, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," abc\\Q abc\\Eabc",[extended, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," abc\\Q abc\\Eabc",[extended]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers"," abc\\Q abc\\Eabc",[extended]))), <<"abcabcabc">> = iolist_to_binary(join(re:split("abcabcabc"," abc\\Q abc\\Eabc",[extended, - trim]))), + trim]))), <<"abcabcabc">> = iolist_to_binary(join(re:split("abcabcabc"," abc\\Q abc\\Eabc",[extended, {parts, - 2}]))), - <<"abcabcabc">> = iolist_to_binary(join(re:split("abcabcabc"," abc\\Q abc\\Eabc",[extended]))), + 2}]))), + <<"abcabcabc">> = iolist_to_binary(join(re:split("abcabcabc"," abc\\Q abc\\Eabc",[extended]))), <<"">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal\\E",[extended,trim]))), + literal\\E",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal\\E",[extended,{parts,2}]))), + literal\\E",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal\\E",[extended]))), + literal\\E",[extended]))), <<"">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal",[extended,trim]))), + literal",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal",[extended,{parts,2}]))), + literal",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal",[extended]))), + literal",[extended]))), <<"">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment literal\\E #more comment - ",[extended,trim]))), + ",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment literal\\E #more comment - ",[extended,{parts,2}]))), + ",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment literal\\E #more comment - ",[extended]))), + ",[extended]))), <<"">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal\\E #more comment",[extended,trim]))), + literal\\E #more comment",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal\\E #more comment",[extended,{parts,2}]))), + literal\\E #more comment",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("abc#not comment literal","abc#comment \\Q#not comment - literal\\E #more comment",[extended]))), - <<"">> = iolist_to_binary(join(re:split("abc\\$xyz","\\Qabc\\$xyz\\E",[trim]))), + literal\\E #more comment",[extended]))), + <<"">> = iolist_to_binary(join(re:split("abc\\$xyz","\\Qabc\\$xyz\\E",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc\\$xyz","\\Qabc\\$xyz\\E",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc\\$xyz","\\Qabc\\$xyz\\E",[]))), - <<"">> = iolist_to_binary(join(re:split("abc$xyz","\\Qabc\\E\\$\\Qxyz\\E",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc\\$xyz","\\Qabc\\$xyz\\E",[]))), + <<"">> = iolist_to_binary(join(re:split("abc$xyz","\\Qabc\\E\\$\\Qxyz\\E",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc$xyz","\\Qabc\\E\\$\\Qxyz\\E",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc$xyz","\\Qabc\\E\\$\\Qxyz\\E",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","\\Aabc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc$xyz","\\Qabc\\E\\$\\Qxyz\\E",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","\\Aabc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","\\Aabc",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc",[]))), - <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","\\Aabc",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\Aabc",[]))), + <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","\\Aabc",[trim]))), <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","\\Aabc",[{parts, - 2}]))), - <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","\\Aabc",[]))), - <<":abc2xyzabc3">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","\\Aabc.",[trim]))), + 2}]))), + <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","\\Aabc",[]))), + <<":abc2xyzabc3">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","\\Aabc.",[trim]))), <<":abc2xyzabc3">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","\\Aabc.",[{parts, - 2}]))), - <<":abc2xyzabc3">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","\\Aabc.",[]))), - <<"::xyz">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","abc.",[trim]))), + 2}]))), + <<":abc2xyzabc3">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","\\Aabc.",[]))), + <<"::xyz">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","abc.",[trim]))), <<":abc2xyzabc3">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","abc.",[{parts, - 2}]))), - <<"::xyz:">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","abc.",[]))), - <<"X:Y">> = iolist_to_binary(join(re:split("XabcdY","a(?x: b c )d",[trim]))), + 2}]))), + <<"::xyz:">> = iolist_to_binary(join(re:split("abc1abc2xyzabc3","abc.",[]))), + <<"X:Y">> = iolist_to_binary(join(re:split("XabcdY","a(?x: b c )d",[trim]))), <<"X:Y">> = iolist_to_binary(join(re:split("XabcdY","a(?x: b c )d",[{parts, - 2}]))), - <<"X:Y">> = iolist_to_binary(join(re:split("XabcdY","a(?x: b c )d",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?x: b c )d",[trim]))), + 2}]))), + <<"X:Y">> = iolist_to_binary(join(re:split("XabcdY","a(?x: b c )d",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?x: b c )d",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?x: b c )d",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?x: b c )d",[]))), - <<"Xa b c d Y">> = iolist_to_binary(join(re:split("Xa b c d Y","a(?x: b c )d",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","a(?x: b c )d",[]))), + <<"Xa b c d Y">> = iolist_to_binary(join(re:split("Xa b c d Y","a(?x: b c )d",[trim]))), <<"Xa b c d Y">> = iolist_to_binary(join(re:split("Xa b c d Y","a(?x: b c )d",[{parts, - 2}]))), - <<"Xa b c d Y">> = iolist_to_binary(join(re:split("Xa b c d Y","a(?x: b c )d",[]))), - <<"X:abc:Y">> = iolist_to_binary(join(re:split("XabcY","((?x)x y z | a b c)",[trim]))), + 2}]))), + <<"Xa b c d Y">> = iolist_to_binary(join(re:split("Xa b c d Y","a(?x: b c )d",[]))), + <<"X:abc:Y">> = iolist_to_binary(join(re:split("XabcY","((?x)x y z | a b c)",[trim]))), <<"X:abc:Y">> = iolist_to_binary(join(re:split("XabcY","((?x)x y z | a b c)",[{parts, - 2}]))), - <<"X:abc:Y">> = iolist_to_binary(join(re:split("XabcY","((?x)x y z | a b c)",[]))), - <<"A:xyz:B">> = iolist_to_binary(join(re:split("AxyzB","((?x)x y z | a b c)",[trim]))), + 2}]))), + <<"X:abc:Y">> = iolist_to_binary(join(re:split("XabcY","((?x)x y z | a b c)",[]))), + <<"A:xyz:B">> = iolist_to_binary(join(re:split("AxyzB","((?x)x y z | a b c)",[trim]))), <<"A:xyz:B">> = iolist_to_binary(join(re:split("AxyzB","((?x)x y z | a b c)",[{parts, - 2}]))), - <<"A:xyz:B">> = iolist_to_binary(join(re:split("AxyzB","((?x)x y z | a b c)",[]))), - <<"X:Y">> = iolist_to_binary(join(re:split("XabCY","(?i)AB(?-i)C",[trim]))), + 2}]))), + <<"A:xyz:B">> = iolist_to_binary(join(re:split("AxyzB","((?x)x y z | a b c)",[]))), + <<"X:Y">> = iolist_to_binary(join(re:split("XabCY","(?i)AB(?-i)C",[trim]))), <<"X:Y">> = iolist_to_binary(join(re:split("XabCY","(?i)AB(?-i)C",[{parts, - 2}]))), - <<"X:Y">> = iolist_to_binary(join(re:split("XabCY","(?i)AB(?-i)C",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i)AB(?-i)C",[trim]))), + 2}]))), + <<"X:Y">> = iolist_to_binary(join(re:split("XabCY","(?i)AB(?-i)C",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i)AB(?-i)C",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i)AB(?-i)C",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i)AB(?-i)C",[]))), - <<"XabcY">> = iolist_to_binary(join(re:split("XabcY","(?i)AB(?-i)C",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(?i)AB(?-i)C",[]))), + <<"XabcY">> = iolist_to_binary(join(re:split("XabcY","(?i)AB(?-i)C",[trim]))), <<"XabcY">> = iolist_to_binary(join(re:split("XabcY","(?i)AB(?-i)C",[{parts, - 2}]))), - <<"XabcY">> = iolist_to_binary(join(re:split("XabcY","(?i)AB(?-i)C",[]))), - <<":abC">> = iolist_to_binary(join(re:split("abCE","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<"XabcY">> = iolist_to_binary(join(re:split("XabcY","(?i)AB(?-i)C",[]))), + <<":abC">> = iolist_to_binary(join(re:split("abCE","((?i)AB(?-i)C|D)E",[trim]))), <<":abC:">> = iolist_to_binary(join(re:split("abCE","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<":abC:">> = iolist_to_binary(join(re:split("abCE","((?i)AB(?-i)C|D)E",[]))), - <<":D">> = iolist_to_binary(join(re:split("DE","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<":abC:">> = iolist_to_binary(join(re:split("abCE","((?i)AB(?-i)C|D)E",[]))), + <<":D">> = iolist_to_binary(join(re:split("DE","((?i)AB(?-i)C|D)E",[trim]))), <<":D:">> = iolist_to_binary(join(re:split("DE","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<":D:">> = iolist_to_binary(join(re:split("DE","((?i)AB(?-i)C|D)E",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<":D:">> = iolist_to_binary(join(re:split("DE","((?i)AB(?-i)C|D)E",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)AB(?-i)C|D)E",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)AB(?-i)C|D)E",[]))), - <<"abcE">> = iolist_to_binary(join(re:split("abcE","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((?i)AB(?-i)C|D)E",[]))), + <<"abcE">> = iolist_to_binary(join(re:split("abcE","((?i)AB(?-i)C|D)E",[trim]))), <<"abcE">> = iolist_to_binary(join(re:split("abcE","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<"abcE">> = iolist_to_binary(join(re:split("abcE","((?i)AB(?-i)C|D)E",[]))), - <<"abCe">> = iolist_to_binary(join(re:split("abCe","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<"abcE">> = iolist_to_binary(join(re:split("abcE","((?i)AB(?-i)C|D)E",[]))), + <<"abCe">> = iolist_to_binary(join(re:split("abCe","((?i)AB(?-i)C|D)E",[trim]))), <<"abCe">> = iolist_to_binary(join(re:split("abCe","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<"abCe">> = iolist_to_binary(join(re:split("abCe","((?i)AB(?-i)C|D)E",[]))), - <<"dE">> = iolist_to_binary(join(re:split("dE","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<"abCe">> = iolist_to_binary(join(re:split("abCe","((?i)AB(?-i)C|D)E",[]))), + <<"dE">> = iolist_to_binary(join(re:split("dE","((?i)AB(?-i)C|D)E",[trim]))), <<"dE">> = iolist_to_binary(join(re:split("dE","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<"dE">> = iolist_to_binary(join(re:split("dE","((?i)AB(?-i)C|D)E",[]))), - <<"De">> = iolist_to_binary(join(re:split("De","((?i)AB(?-i)C|D)E",[trim]))), + 2}]))), + <<"dE">> = iolist_to_binary(join(re:split("dE","((?i)AB(?-i)C|D)E",[]))), + <<"De">> = iolist_to_binary(join(re:split("De","((?i)AB(?-i)C|D)E",[trim]))), <<"De">> = iolist_to_binary(join(re:split("De","((?i)AB(?-i)C|D)E",[{parts, - 2}]))), - <<"De">> = iolist_to_binary(join(re:split("De","((?i)AB(?-i)C|D)E",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[trim]))), + 2}]))), + <<"De">> = iolist_to_binary(join(re:split("De","((?i)AB(?-i)C|D)E",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[]))), - <<"a:bc">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[]))), + <<"a:bc">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[trim]))), <<"a:bc:">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[{parts, - 2}]))), - <<"a:bc:">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[]))), + 2}]))), + <<"a:bc:">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[]))), <<":abc">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[dotall, - trim]))), + trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[dotall, {parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[dotall]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abc123abc","(.*)\\d+\\1",[dotall]))), <<"a:bc">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[dotall, - trim]))), + trim]))), <<"a:bc:">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[dotall, {parts, - 2}]))), - <<"a:bc:">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[dotall]))), - <<":abc:abc">> = iolist_to_binary(join(re:split("abc123abc","((.*))\\d+\\1",[trim]))), + 2}]))), + <<"a:bc:">> = iolist_to_binary(join(re:split("abc123bc","(.*)\\d+\\1",[dotall]))), + <<":abc:abc">> = iolist_to_binary(join(re:split("abc123abc","((.*))\\d+\\1",[trim]))), <<":abc:abc:">> = iolist_to_binary(join(re:split("abc123abc","((.*))\\d+\\1",[{parts, - 2}]))), - <<":abc:abc:">> = iolist_to_binary(join(re:split("abc123abc","((.*))\\d+\\1",[]))), - <<"a:bc:bc">> = iolist_to_binary(join(re:split("abc123bc","((.*))\\d+\\1",[trim]))), + 2}]))), + <<":abc:abc:">> = iolist_to_binary(join(re:split("abc123abc","((.*))\\d+\\1",[]))), + <<"a:bc:bc">> = iolist_to_binary(join(re:split("abc123bc","((.*))\\d+\\1",[trim]))), <<"a:bc:bc:">> = iolist_to_binary(join(re:split("abc123bc","((.*))\\d+\\1",[{parts, - 2}]))), - <<"a:bc:bc:">> = iolist_to_binary(join(re:split("abc123bc","((.*))\\d+\\1",[]))), + 2}]))), + <<"a:bc:bc:">> = iolist_to_binary(join(re:split("abc123bc","((.*))\\d+\\1",[]))), <<"">> = iolist_to_binary(join(re:split("a123::a123","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28581,7 +28581,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::">> = iolist_to_binary(join(re:split("a123::a123","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28590,7 +28590,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::">> = iolist_to_binary(join(re:split("a123::a123","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28599,7 +28599,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"">> = iolist_to_binary(join(re:split("a123:b342::abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28608,7 +28608,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::">> = iolist_to_binary(join(re:split("a123:b342::abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28617,7 +28617,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::">> = iolist_to_binary(join(re:split("a123:b342::abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28626,7 +28626,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"">> = iolist_to_binary(join(re:split("a123:b342::324e:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28635,7 +28635,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::">> = iolist_to_binary(join(re:split("a123:b342::324e:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28644,7 +28644,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::">> = iolist_to_binary(join(re:split("a123:b342::324e:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28653,7 +28653,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"">> = iolist_to_binary(join(re:split("a123:ddde:b342::324e:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28662,7 +28662,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::">> = iolist_to_binary(join(re:split("a123:ddde:b342::324e:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28671,7 +28671,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::">> = iolist_to_binary(join(re:split("a123:ddde:b342::324e:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28680,7 +28680,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"">> = iolist_to_binary(join(re:split("a123:ddde:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28689,7 +28689,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::">> = iolist_to_binary(join(re:split("a123:ddde:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28698,7 +28698,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::">> = iolist_to_binary(join(re:split("a123:ddde:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28707,7 +28707,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"">> = iolist_to_binary(join(re:split("a123:ddde:9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28716,7 +28716,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::">> = iolist_to_binary(join(re:split("a123:ddde:9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28725,7 +28725,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::">> = iolist_to_binary(join(re:split("a123:ddde:9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28734,7 +28734,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28743,7 +28743,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28752,7 +28752,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28761,7 +28761,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"1:2:3:4:5:6:7:8">> = iolist_to_binary(join(re:split("1:2:3:4:5:6:7:8","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28770,7 +28770,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"1:2:3:4:5:6:7:8">> = iolist_to_binary(join(re:split("1:2:3:4:5:6:7:8","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28779,7 +28779,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"1:2:3:4:5:6:7:8">> = iolist_to_binary(join(re:split("1:2:3:4:5:6:7:8","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28788,7 +28788,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"a123:bce:ddde:9999:b342::324e:dcba:abcd">> = iolist_to_binary(join(re:split("a123:bce:ddde:9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28797,7 +28797,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"a123:bce:ddde:9999:b342::324e:dcba:abcd">> = iolist_to_binary(join(re:split("a123:bce:ddde:9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28806,7 +28806,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"a123:bce:ddde:9999:b342::324e:dcba:abcd">> = iolist_to_binary(join(re:split("a123:bce:ddde:9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28815,7 +28815,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"a123::9999:b342::324e:dcba:abcd">> = iolist_to_binary(join(re:split("a123::9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28824,7 +28824,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"a123::9999:b342::324e:dcba:abcd">> = iolist_to_binary(join(re:split("a123::9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28833,7 +28833,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"a123::9999:b342::324e:dcba:abcd">> = iolist_to_binary(join(re:split("a123::9999:b342::324e:dcba:abcd","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28842,7 +28842,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"abcde:2:3:4:5:6:7:8">> = iolist_to_binary(join(re:split("abcde:2:3:4:5:6:7:8","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28851,7 +28851,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"abcde:2:3:4:5:6:7:8">> = iolist_to_binary(join(re:split("abcde:2:3:4:5:6:7:8","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28860,7 +28860,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"abcde:2:3:4:5:6:7:8">> = iolist_to_binary(join(re:split("abcde:2:3:4:5:6:7:8","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28869,7 +28869,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"::1">> = iolist_to_binary(join(re:split("::1","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28878,7 +28878,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"::1">> = iolist_to_binary(join(re:split("::1","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28887,7 +28887,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"::1">> = iolist_to_binary(join(re:split("::1","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28896,7 +28896,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"abcd:fee0:123::">> = iolist_to_binary(join(re:split("abcd:fee0:123::","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28905,7 +28905,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"abcd:fee0:123::">> = iolist_to_binary(join(re:split("abcd:fee0:123::","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28914,7 +28914,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"abcd:fee0:123::">> = iolist_to_binary(join(re:split("abcd:fee0:123::","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28923,7 +28923,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<":1">> = iolist_to_binary(join(re:split(":1","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28932,7 +28932,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<":1">> = iolist_to_binary(join(re:split(":1","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28941,7 +28941,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<":1">> = iolist_to_binary(join(re:split(":1","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28950,7 +28950,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), + ",[extended,caseless]))), <<"1:">> = iolist_to_binary(join(re:split("1:","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28959,7 +28959,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,trim]))), + ",[extended,caseless,trim]))), <<"1:">> = iolist_to_binary(join(re:split("1:","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28968,7 +28968,7 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless,{parts,2}]))), + ",[extended,caseless,{parts,2}]))), <<"1:">> = iolist_to_binary(join(re:split("1:","^(?!:) # colon disallowed at start (?: # start of item (?: [0-9a-f]{1,4} | # 1-4 hex digits or @@ -28977,3098 +28977,3147 @@ run33() -> ){1,7} # end item; 1-7 of them required [0-9a-f]{1,4} $ # final hex number at end of string (?(1)|.) # check that there was an empty component - ",[extended,caseless]))), - <<"">> = iolist_to_binary(join(re:split("z","[z\\Qa-d]\\E]",[trim]))), + ",[extended,caseless]))), + <<"">> = iolist_to_binary(join(re:split("z","[z\\Qa-d]\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("z","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("z","[z\\Qa-d]\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("a","[z\\Qa-d]\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("z","[z\\Qa-d]\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("a","[z\\Qa-d]\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","[z\\Qa-d]\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("-","[z\\Qa-d]\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","[z\\Qa-d]\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("-","[z\\Qa-d]\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","[z\\Qa-d]\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("d","[z\\Qa-d]\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","[z\\Qa-d]\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("d","[z\\Qa-d]\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("d","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("d","[z\\Qa-d]\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("]","[z\\Qa-d]\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("d","[z\\Qa-d]\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("]","[z\\Qa-d]\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("]","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("]","[z\\Qa-d]\\E]",[]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[z\\Qa-d]\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("]","[z\\Qa-d]\\E]",[]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[z\\Qa-d]\\E]",[trim]))), <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[z\\Qa-d]\\E]",[]))), - <<"b">> = iolist_to_binary(join(re:split("b","[z\\Qa-d]\\E]",[trim]))), + 2}]))), + <<"*** F:ilers">> = iolist_to_binary(join(re:split("*** Failers","[z\\Qa-d]\\E]",[]))), + <<"b">> = iolist_to_binary(join(re:split("b","[z\\Qa-d]\\E]",[trim]))), <<"b">> = iolist_to_binary(join(re:split("b","[z\\Qa-d]\\E]",[{parts, - 2}]))), - <<"b">> = iolist_to_binary(join(re:split("b","[z\\Qa-d]\\E]",[]))), + 2}]))), + <<"b">> = iolist_to_binary(join(re:split("b","[z\\Qa-d]\\E]",[]))), ok. run34() -> - <<"">> = iolist_to_binary(join(re:split("z","[\\z\\C]",[trim]))), + <<"">> = iolist_to_binary(join(re:split("z","[\\z\\C]",[trim]))), <<":">> = iolist_to_binary(join(re:split("z","[\\z\\C]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("z","[\\z\\C]",[]))), - <<"">> = iolist_to_binary(join(re:split("C","[\\z\\C]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("z","[\\z\\C]",[]))), + <<"">> = iolist_to_binary(join(re:split("C","[\\z\\C]",[trim]))), <<":">> = iolist_to_binary(join(re:split("C","[\\z\\C]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("C","[\\z\\C]",[]))), - <<"">> = iolist_to_binary(join(re:split("M","\\M",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("C","[\\z\\C]",[]))), + <<"">> = iolist_to_binary(join(re:split("M","\\M",[trim]))), <<":">> = iolist_to_binary(join(re:split("M","\\M",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("M","\\M",[]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("M","\\M",[]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[trim]))), <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[]))), - <<"„XAZ">> = iolist_to_binary(join(re:split("„XAZXB","(?<=Z)X.",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a+)*b",[]))), + <<"„XAZ">> = iolist_to_binary(join(re:split("„XAZXB","(?<=Z)X.",[trim]))), <<"„XAZ:">> = iolist_to_binary(join(re:split("„XAZXB","(?<=Z)X.",[{parts, - 2}]))), - <<"„XAZ:">> = iolist_to_binary(join(re:split("„XAZXB","(?<=Z)X.",[]))), - <<"">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[trim]))), + 2}]))), + <<"„XAZ:">> = iolist_to_binary(join(re:split("„XAZXB","(?<=Z)X.",[]))), + <<"">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[]))), - <<"">> = iolist_to_binary(join(re:split("ab cddefg","ab cd(?x) de fg",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab cd defg","ab cd (?x) de fg",[]))), + <<"">> = iolist_to_binary(join(re:split("ab cddefg","ab cd(?x) de fg",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab cddefg","ab cd(?x) de fg",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab cddefg","ab cd(?x) de fg",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","ab cd(?x) de fg",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab cddefg","ab cd(?x) de fg",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","ab cd(?x) de fg",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","ab cd(?x) de fg",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","ab cd(?x) de fg",[]))), - <<"abcddefg">> = iolist_to_binary(join(re:split("abcddefg","ab cd(?x) de fg",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","ab cd(?x) de fg",[]))), + <<"abcddefg">> = iolist_to_binary(join(re:split("abcddefg","ab cd(?x) de fg",[trim]))), <<"abcddefg">> = iolist_to_binary(join(re:split("abcddefg","ab cd(?x) de fg",[{parts, - 2}]))), - <<"abcddefg">> = iolist_to_binary(join(re:split("abcddefg","ab cd(?x) de fg",[]))), - <<"foo:bar:X">> = iolist_to_binary(join(re:split("foobarX","(?<![^f]oo)(bar)",[trim]))), + 2}]))), + <<"abcddefg">> = iolist_to_binary(join(re:split("abcddefg","ab cd(?x) de fg",[]))), + <<"foo:bar:X">> = iolist_to_binary(join(re:split("foobarX","(?<![^f]oo)(bar)",[trim]))), <<"foo:bar:X">> = iolist_to_binary(join(re:split("foobarX","(?<![^f]oo)(bar)",[{parts, - 2}]))), - <<"foo:bar:X">> = iolist_to_binary(join(re:split("foobarX","(?<![^f]oo)(bar)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f]oo)(bar)",[trim]))), + 2}]))), + <<"foo:bar:X">> = iolist_to_binary(join(re:split("foobarX","(?<![^f]oo)(bar)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f]oo)(bar)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f]oo)(bar)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f]oo)(bar)",[]))), - <<"boobarX">> = iolist_to_binary(join(re:split("boobarX","(?<![^f]oo)(bar)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f]oo)(bar)",[]))), + <<"boobarX">> = iolist_to_binary(join(re:split("boobarX","(?<![^f]oo)(bar)",[trim]))), <<"boobarX">> = iolist_to_binary(join(re:split("boobarX","(?<![^f]oo)(bar)",[{parts, - 2}]))), - <<"boobarX">> = iolist_to_binary(join(re:split("boobarX","(?<![^f]oo)(bar)",[]))), - <<"off">> = iolist_to_binary(join(re:split("offX","(?<![^f])X",[trim]))), + 2}]))), + <<"boobarX">> = iolist_to_binary(join(re:split("boobarX","(?<![^f]oo)(bar)",[]))), + <<"off">> = iolist_to_binary(join(re:split("offX","(?<![^f])X",[trim]))), <<"off:">> = iolist_to_binary(join(re:split("offX","(?<![^f])X",[{parts, - 2}]))), - <<"off:">> = iolist_to_binary(join(re:split("offX","(?<![^f])X",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f])X",[trim]))), + 2}]))), + <<"off:">> = iolist_to_binary(join(re:split("offX","(?<![^f])X",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f])X",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f])X",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f])X",[]))), - <<"onyX">> = iolist_to_binary(join(re:split("onyX","(?<![^f])X",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<![^f])X",[]))), + <<"onyX">> = iolist_to_binary(join(re:split("onyX","(?<![^f])X",[trim]))), <<"onyX">> = iolist_to_binary(join(re:split("onyX","(?<![^f])X",[{parts, - 2}]))), - <<"onyX">> = iolist_to_binary(join(re:split("onyX","(?<![^f])X",[]))), - <<"ony">> = iolist_to_binary(join(re:split("onyX","(?<=[^f])X",[trim]))), + 2}]))), + <<"onyX">> = iolist_to_binary(join(re:split("onyX","(?<![^f])X",[]))), + <<"ony">> = iolist_to_binary(join(re:split("onyX","(?<=[^f])X",[trim]))), <<"ony:">> = iolist_to_binary(join(re:split("onyX","(?<=[^f])X",[{parts, - 2}]))), - <<"ony:">> = iolist_to_binary(join(re:split("onyX","(?<=[^f])X",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^f])X",[trim]))), + 2}]))), + <<"ony:">> = iolist_to_binary(join(re:split("onyX","(?<=[^f])X",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^f])X",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^f])X",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^f])X",[]))), - <<"offX">> = iolist_to_binary(join(re:split("offX","(?<=[^f])X",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^f])X",[]))), + <<"offX">> = iolist_to_binary(join(re:split("offX","(?<=[^f])X",[trim]))), <<"offX">> = iolist_to_binary(join(re:split("offX","(?<=[^f])X",[{parts, - 2}]))), - <<"offX">> = iolist_to_binary(join(re:split("offX","(?<=[^f])X",[]))), + 2}]))), + <<"offX">> = iolist_to_binary(join(re:split("offX","(?<=[^f])X",[]))), <<"a :b :c">> = iolist_to_binary(join(re:split("a b -c","^",[multiline,trim]))), +c","^",[multiline,trim]))), <<"a :b c">> = iolist_to_binary(join(re:split("a b -c","^",[multiline,{parts,2}]))), +c","^",[multiline,{parts,2}]))), <<"a :b :c">> = iolist_to_binary(join(re:split("a b -c","^",[multiline]))), +c","^",[multiline]))), <<"">> = iolist_to_binary(join(re:split("","^",[multiline, - trim]))), + trim]))), <<"">> = iolist_to_binary(join(re:split("","^",[multiline, {parts, - 2}]))), - <<"">> = iolist_to_binary(join(re:split("","^",[multiline]))), + 2}]))), + <<"">> = iolist_to_binary(join(re:split("","^",[multiline]))), <<"A C :C">> = iolist_to_binary(join(re:split("A C -C","(?<=C\\n)^",[multiline,trim]))), +C","(?<=C\\n)^",[multiline,trim]))), <<"A C :C">> = iolist_to_binary(join(re:split("A C -C","(?<=C\\n)^",[multiline,{parts,2}]))), +C","(?<=C\\n)^",[multiline,{parts,2}]))), <<"A C :C">> = iolist_to_binary(join(re:split("A C -C","(?<=C\\n)^",[multiline]))), - <<":X">> = iolist_to_binary(join(re:split("bXaX","(?:(?(1)a|b)(X))+",[trim]))), +C","(?<=C\\n)^",[multiline]))), + <<":X">> = iolist_to_binary(join(re:split("bXaX","(?:(?(1)a|b)(X))+",[trim]))), <<":X:">> = iolist_to_binary(join(re:split("bXaX","(?:(?(1)a|b)(X))+",[{parts, - 2}]))), - <<":X:">> = iolist_to_binary(join(re:split("bXaX","(?:(?(1)a|b)(X))+",[]))), - <<":Y">> = iolist_to_binary(join(re:split("bXXaYYaY","(?:(?(1)\\1a|b)(X|Y))+",[trim]))), + 2}]))), + <<":X:">> = iolist_to_binary(join(re:split("bXaX","(?:(?(1)a|b)(X))+",[]))), + <<":Y">> = iolist_to_binary(join(re:split("bXXaYYaY","(?:(?(1)\\1a|b)(X|Y))+",[trim]))), <<":Y:">> = iolist_to_binary(join(re:split("bXXaYYaY","(?:(?(1)\\1a|b)(X|Y))+",[{parts, - 2}]))), - <<":Y:">> = iolist_to_binary(join(re:split("bXXaYYaY","(?:(?(1)\\1a|b)(X|Y))+",[]))), - <<":X:YaXXaX">> = iolist_to_binary(join(re:split("bXYaXXaX","(?:(?(1)\\1a|b)(X|Y))+",[trim]))), + 2}]))), + <<":Y:">> = iolist_to_binary(join(re:split("bXXaYYaY","(?:(?(1)\\1a|b)(X|Y))+",[]))), + <<":X:YaXXaX">> = iolist_to_binary(join(re:split("bXYaXXaX","(?:(?(1)\\1a|b)(X|Y))+",[trim]))), <<":X:YaXXaX">> = iolist_to_binary(join(re:split("bXYaXXaX","(?:(?(1)\\1a|b)(X|Y))+",[{parts, - 2}]))), - <<":X:YaXXaX">> = iolist_to_binary(join(re:split("bXYaXXaX","(?:(?(1)\\1a|b)(X|Y))+",[]))), - <<"::::::::::X:XaYYaY">> = iolist_to_binary(join(re:split("bXXaYYaY","()()()()()()()()()(?:(?(10)\\10a|b)(X|Y))+",[trim]))), + 2}]))), + <<":X:YaXXaX">> = iolist_to_binary(join(re:split("bXYaXXaX","(?:(?(1)\\1a|b)(X|Y))+",[]))), + <<"::::::::::X:XaYYaY">> = iolist_to_binary(join(re:split("bXXaYYaY","()()()()()()()()()(?:(?(10)\\10a|b)(X|Y))+",[trim]))), <<"::::::::::X:XaYYaY">> = iolist_to_binary(join(re:split("bXXaYYaY","()()()()()()()()()(?:(?(10)\\10a|b)(X|Y))+",[{parts, - 2}]))), - <<"::::::::::X:XaYYaY">> = iolist_to_binary(join(re:split("bXXaYYaY","()()()()()()()()()(?:(?(10)\\10a|b)(X|Y))+",[]))), - <<"">> = iolist_to_binary(join(re:split("abc]","[[,abc,]+]",[trim]))), + 2}]))), + <<"::::::::::X:XaYYaY">> = iolist_to_binary(join(re:split("bXXaYYaY","()()()()()()()()()(?:(?(10)\\10a|b)(X|Y))+",[]))), + <<"">> = iolist_to_binary(join(re:split("abc]","[[,abc,]+]",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc]","[[,abc,]+]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc]","[[,abc,]+]",[]))), - <<"">> = iolist_to_binary(join(re:split("a,b]","[[,abc,]+]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc]","[[,abc,]+]",[]))), + <<"">> = iolist_to_binary(join(re:split("a,b]","[[,abc,]+]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a,b]","[[,abc,]+]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a,b]","[[,abc,]+]",[]))), - <<"">> = iolist_to_binary(join(re:split("[a,b,c]","[[,abc,]+]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a,b]","[[,abc,]+]",[]))), + <<"">> = iolist_to_binary(join(re:split("[a,b,c]","[[,abc,]+]",[trim]))), <<":">> = iolist_to_binary(join(re:split("[a,b,c]","[[,abc,]+]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("[a,b,c]","[[,abc,]+]",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("[a,b,c]","[[,abc,]+]",[]))), <<"A:B">> = iolist_to_binary(join(re:split("A B","(?-x: )",[extended, - trim]))), + trim]))), <<"A:B">> = iolist_to_binary(join(re:split("A B","(?-x: )",[extended, {parts, - 2}]))), - <<"A:B">> = iolist_to_binary(join(re:split("A B","(?-x: )",[extended]))), - <<"A:B">> = iolist_to_binary(join(re:split("A # B","(?x)(?-x: \\s*#\\s*)",[trim]))), + 2}]))), + <<"A:B">> = iolist_to_binary(join(re:split("A B","(?-x: )",[extended]))), + <<"A:B">> = iolist_to_binary(join(re:split("A # B","(?x)(?-x: \\s*#\\s*)",[trim]))), <<"A:B">> = iolist_to_binary(join(re:split("A # B","(?x)(?-x: \\s*#\\s*)",[{parts, - 2}]))), - <<"A:B">> = iolist_to_binary(join(re:split("A # B","(?x)(?-x: \\s*#\\s*)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x)(?-x: \\s*#\\s*)",[trim]))), + 2}]))), + <<"A:B">> = iolist_to_binary(join(re:split("A # B","(?x)(?-x: \\s*#\\s*)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x)(?-x: \\s*#\\s*)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x)(?-x: \\s*#\\s*)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x)(?-x: \\s*#\\s*)",[]))), - <<"#">> = iolist_to_binary(join(re:split("#","(?x)(?-x: \\s*#\\s*)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x)(?-x: \\s*#\\s*)",[]))), + <<"#">> = iolist_to_binary(join(re:split("#","(?x)(?-x: \\s*#\\s*)",[trim]))), <<"#">> = iolist_to_binary(join(re:split("#","(?x)(?-x: \\s*#\\s*)",[{parts, - 2}]))), - <<"#">> = iolist_to_binary(join(re:split("#","(?x)(?-x: \\s*#\\s*)",[]))), - <<"A">> = iolist_to_binary(join(re:split("A #include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), + 2}]))), + <<"#">> = iolist_to_binary(join(re:split("#","(?x)(?-x: \\s*#\\s*)",[]))), + <<"A">> = iolist_to_binary(join(re:split("A #include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), <<"A:">> = iolist_to_binary(join(re:split("A #include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[{parts, - 2}]))), - <<"A:">> = iolist_to_binary(join(re:split("A #include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), + 2}]))), + <<"A:">> = iolist_to_binary(join(re:split("A #include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), - <<"A#include">> = iolist_to_binary(join(re:split("A#include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), + <<"A#include">> = iolist_to_binary(join(re:split("A#include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), <<"A#include">> = iolist_to_binary(join(re:split("A#include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[{parts, - 2}]))), - <<"A#include">> = iolist_to_binary(join(re:split("A#include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), - <<"A #Include">> = iolist_to_binary(join(re:split("A #Include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), + 2}]))), + <<"A#include">> = iolist_to_binary(join(re:split("A#include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), + <<"A #Include">> = iolist_to_binary(join(re:split("A #Include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[trim]))), <<"A #Include">> = iolist_to_binary(join(re:split("A #Include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[{parts, - 2}]))), - <<"A #Include">> = iolist_to_binary(join(re:split("A #Include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), + 2}]))), + <<"A #Include">> = iolist_to_binary(join(re:split("A #Include","(?x-is)(?:(?-ixs) \\s*#\\s*) include",[]))), ok. run35() -> - <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b*\\w",[trim]))), + <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","a*b*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","a*b*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","a*b*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","a*b*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","a*b*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","a*b*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","a*b*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*b*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a*b*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b?\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a*b*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b?\\w",[trim]))), <<":bb">> = iolist_to_binary(join(re:split("aaabbbb","a*b?\\w",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaabbbb","a*b?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","a*b?\\w",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaabbbb","a*b?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","a*b?\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","a*b?\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","a*b?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","a*b?\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","a*b?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","a*b?\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*b?\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a*b?\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,4}\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a*b?\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,4}\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,4}\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,4}\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","a*b{0,4}\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,4}\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","a*b{0,4}\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","a*b{0,4}\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","a*b{0,4}\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","a*b{0,4}\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","a*b{0,4}\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","a*b{0,4}\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*b{0,4}\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a*b{0,4}\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,}\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a*b{0,4}\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,}\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,}\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,}\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","a*b{0,}\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaabbbb","a*b{0,}\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","a*b{0,}\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","a*b{0,}\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","a*b{0,}\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","a*b{0,}\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","a*b{0,}\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","a*b{0,}\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*b{0,}\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a*b{0,}\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("0a","a*\\d*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a*b{0,}\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("0a","a*\\d*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("0a","a*\\d*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("0a","a*\\d*\\w",[]))), - <<"">> = iolist_to_binary(join(re:split("a","a*\\d*\\w",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("0a","a*\\d*\\w",[]))), + <<"">> = iolist_to_binary(join(re:split("a","a*\\d*\\w",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*\\d*\\w",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a*\\d*\\w",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a*\\d*\\w",[]))), <<"">> = iolist_to_binary(join(re:split("a","a*b *\\w",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*b *\\w",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a*b *\\w",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a*b *\\w",[extended]))), <<"">> = iolist_to_binary(join(re:split("a","a*b#comment - *\\w",[extended,trim]))), + *\\w",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("a","a*b#comment - *\\w",[extended,{parts,2}]))), + *\\w",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("a","a*b#comment - *\\w",[extended]))), + *\\w",[extended]))), <<"">> = iolist_to_binary(join(re:split("a","a* b *\\w",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("a","a* b *\\w",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","a* b *\\w",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","a* b *\\w",[extended]))), <<":: pqr">> = iolist_to_binary(join(re:split("abc=xyz\\ -pqr","^\\w+=.*(\\\\\\n.*)*",[trim]))), +pqr","^\\w+=.*(\\\\\\n.*)*",[trim]))), <<":: pqr">> = iolist_to_binary(join(re:split("abc=xyz\\ -pqr","^\\w+=.*(\\\\\\n.*)*",[{parts,2}]))), +pqr","^\\w+=.*(\\\\\\n.*)*",[{parts,2}]))), <<":: pqr">> = iolist_to_binary(join(re:split("abc=xyz\\ -pqr","^\\w+=.*(\\\\\\n.*)*",[]))), - <<":abcd">> = iolist_to_binary(join(re:split("abcd:","(?=(\\w+))\\1:",[trim]))), +pqr","^\\w+=.*(\\\\\\n.*)*",[]))), + <<":abcd">> = iolist_to_binary(join(re:split("abcd:","(?=(\\w+))\\1:",[trim]))), <<":abcd:">> = iolist_to_binary(join(re:split("abcd:","(?=(\\w+))\\1:",[{parts, - 2}]))), - <<":abcd:">> = iolist_to_binary(join(re:split("abcd:","(?=(\\w+))\\1:",[]))), - <<":abcd">> = iolist_to_binary(join(re:split("abcd:","^(?=(\\w+))\\1:",[trim]))), + 2}]))), + <<":abcd:">> = iolist_to_binary(join(re:split("abcd:","(?=(\\w+))\\1:",[]))), + <<":abcd">> = iolist_to_binary(join(re:split("abcd:","^(?=(\\w+))\\1:",[trim]))), <<":abcd:">> = iolist_to_binary(join(re:split("abcd:","^(?=(\\w+))\\1:",[{parts, - 2}]))), - <<":abcd:">> = iolist_to_binary(join(re:split("abcd:","^(?=(\\w+))\\1:",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","^\\Eabc",[trim]))), + 2}]))), + <<":abcd:">> = iolist_to_binary(join(re:split("abcd:","^(?=(\\w+))\\1:",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","^\\Eabc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^\\Eabc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","^\\Eabc",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^[\\Eabc]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","^\\Eabc",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^[\\Eabc]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","^[\\Eabc]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","^[\\Eabc]",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\Eabc]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","^[\\Eabc]",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\Eabc]",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\Eabc]",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\Eabc]",[]))), - <<"E">> = iolist_to_binary(join(re:split("E","^[\\Eabc]",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\Eabc]",[]))), + <<"E">> = iolist_to_binary(join(re:split("E","^[\\Eabc]",[trim]))), <<"E">> = iolist_to_binary(join(re:split("E","^[\\Eabc]",[{parts, - 2}]))), - <<"E">> = iolist_to_binary(join(re:split("E","^[\\Eabc]",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^[a-\\Ec]",[trim]))), + 2}]))), + <<"E">> = iolist_to_binary(join(re:split("E","^[\\Eabc]",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^[a-\\Ec]",[trim]))), <<":">> = iolist_to_binary(join(re:split("b","^[a-\\Ec]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("b","^[a-\\Ec]",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a-\\Ec]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("b","^[a-\\Ec]",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a-\\Ec]",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a-\\Ec]",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a-\\Ec]",[]))), - <<"-">> = iolist_to_binary(join(re:split("-","^[a-\\Ec]",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a-\\Ec]",[]))), + <<"-">> = iolist_to_binary(join(re:split("-","^[a-\\Ec]",[trim]))), <<"-">> = iolist_to_binary(join(re:split("-","^[a-\\Ec]",[{parts, - 2}]))), - <<"-">> = iolist_to_binary(join(re:split("-","^[a-\\Ec]",[]))), - <<"E">> = iolist_to_binary(join(re:split("E","^[a-\\Ec]",[trim]))), + 2}]))), + <<"-">> = iolist_to_binary(join(re:split("-","^[a-\\Ec]",[]))), + <<"E">> = iolist_to_binary(join(re:split("E","^[a-\\Ec]",[trim]))), <<"E">> = iolist_to_binary(join(re:split("E","^[a-\\Ec]",[{parts, - 2}]))), - <<"E">> = iolist_to_binary(join(re:split("E","^[a-\\Ec]",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^[a\\E\\E-\\Ec]",[trim]))), + 2}]))), + <<"E">> = iolist_to_binary(join(re:split("E","^[a-\\Ec]",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^[a\\E\\E-\\Ec]",[trim]))), <<":">> = iolist_to_binary(join(re:split("b","^[a\\E\\E-\\Ec]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("b","^[a\\E\\E-\\Ec]",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a\\E\\E-\\Ec]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("b","^[a\\E\\E-\\Ec]",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a\\E\\E-\\Ec]",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a\\E\\E-\\Ec]",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a\\E\\E-\\Ec]",[]))), - <<"-">> = iolist_to_binary(join(re:split("-","^[a\\E\\E-\\Ec]",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[a\\E\\E-\\Ec]",[]))), + <<"-">> = iolist_to_binary(join(re:split("-","^[a\\E\\E-\\Ec]",[trim]))), <<"-">> = iolist_to_binary(join(re:split("-","^[a\\E\\E-\\Ec]",[{parts, - 2}]))), - <<"-">> = iolist_to_binary(join(re:split("-","^[a\\E\\E-\\Ec]",[]))), - <<"E">> = iolist_to_binary(join(re:split("E","^[a\\E\\E-\\Ec]",[trim]))), + 2}]))), + <<"-">> = iolist_to_binary(join(re:split("-","^[a\\E\\E-\\Ec]",[]))), + <<"E">> = iolist_to_binary(join(re:split("E","^[a\\E\\E-\\Ec]",[trim]))), <<"E">> = iolist_to_binary(join(re:split("E","^[a\\E\\E-\\Ec]",[{parts, - 2}]))), - <<"E">> = iolist_to_binary(join(re:split("E","^[a\\E\\E-\\Ec]",[]))), - <<"">> = iolist_to_binary(join(re:split("b","^[\\E\\Qa\\E-\\Qz\\E]+",[trim]))), + 2}]))), + <<"E">> = iolist_to_binary(join(re:split("E","^[a\\E\\E-\\Ec]",[]))), + <<"">> = iolist_to_binary(join(re:split("b","^[\\E\\Qa\\E-\\Qz\\E]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("b","^[\\E\\Qa\\E-\\Qz\\E]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("b","^[\\E\\Qa\\E-\\Qz\\E]+",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\E\\Qa\\E-\\Qz\\E]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("b","^[\\E\\Qa\\E-\\Qz\\E]+",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\E\\Qa\\E-\\Qz\\E]+",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\E\\Qa\\E-\\Qz\\E]+",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\E\\Qa\\E-\\Qz\\E]+",[]))), - <<"-">> = iolist_to_binary(join(re:split("-","^[\\E\\Qa\\E-\\Qz\\E]+",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\E\\Qa\\E-\\Qz\\E]+",[]))), + <<"-">> = iolist_to_binary(join(re:split("-","^[\\E\\Qa\\E-\\Qz\\E]+",[trim]))), <<"-">> = iolist_to_binary(join(re:split("-","^[\\E\\Qa\\E-\\Qz\\E]+",[{parts, - 2}]))), - <<"-">> = iolist_to_binary(join(re:split("-","^[\\E\\Qa\\E-\\Qz\\E]+",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^[a\\Q]bc\\E]",[trim]))), + 2}]))), + <<"-">> = iolist_to_binary(join(re:split("-","^[\\E\\Qa\\E-\\Qz\\E]+",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^[a\\Q]bc\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","^[a\\Q]bc\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","^[a\\Q]bc\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("]","^[a\\Q]bc\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","^[a\\Q]bc\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("]","^[a\\Q]bc\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("]","^[a\\Q]bc\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("]","^[a\\Q]bc\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("c","^[a\\Q]bc\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("]","^[a\\Q]bc\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("c","^[a\\Q]bc\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("c","^[a\\Q]bc\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("c","^[a\\Q]bc\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("a","^[a-\\Q\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("c","^[a\\Q]bc\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("a","^[a-\\Q\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","^[a-\\Q\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","^[a-\\Q\\E]",[]))), - <<"">> = iolist_to_binary(join(re:split("-","^[a-\\Q\\E]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","^[a-\\Q\\E]",[]))), + <<"">> = iolist_to_binary(join(re:split("-","^[a-\\Q\\E]",[trim]))), <<":">> = iolist_to_binary(join(re:split("-","^[a-\\Q\\E]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("-","^[a-\\Q\\E]",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaa","^(a()*)*",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("-","^[a-\\Q\\E]",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaa","^(a()*)*",[trim]))), <<":a::">> = iolist_to_binary(join(re:split("aaaa","^(a()*)*",[{parts, - 2}]))), - <<":a::">> = iolist_to_binary(join(re:split("aaaa","^(a()*)*",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))*)*",[trim]))), + 2}]))), + <<":a::">> = iolist_to_binary(join(re:split("aaaa","^(a()*)*",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))*)*",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))*)*",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))*)*",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))*)*",[]))), ok. run36() -> - <<":a">> = iolist_to_binary(join(re:split("aaaa","^(a()+)+",[trim]))), + <<":a">> = iolist_to_binary(join(re:split("aaaa","^(a()+)+",[trim]))), <<":a::">> = iolist_to_binary(join(re:split("aaaa","^(a()+)+",[{parts, - 2}]))), - <<":a::">> = iolist_to_binary(join(re:split("aaaa","^(a()+)+",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))+)+",[trim]))), + 2}]))), + <<":a::">> = iolist_to_binary(join(re:split("aaaa","^(a()+)+",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))+)+",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))+)+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))+)+",[]))), - <<":a">> = iolist_to_binary(join(re:split("abbD","(a){0,3}(?(1)b|(c|))*D",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^(?:a(?:(?:))+)+",[]))), + <<":a">> = iolist_to_binary(join(re:split("abbD","(a){0,3}(?(1)b|(c|))*D",[trim]))), <<":a::">> = iolist_to_binary(join(re:split("abbD","(a){0,3}(?(1)b|(c|))*D",[{parts, - 2}]))), - <<":a::">> = iolist_to_binary(join(re:split("abbD","(a){0,3}(?(1)b|(c|))*D",[]))), - <<"">> = iolist_to_binary(join(re:split("ccccD","(a){0,3}(?(1)b|(c|))*D",[trim]))), + 2}]))), + <<":a::">> = iolist_to_binary(join(re:split("abbD","(a){0,3}(?(1)b|(c|))*D",[]))), + <<"">> = iolist_to_binary(join(re:split("ccccD","(a){0,3}(?(1)b|(c|))*D",[trim]))), <<":::">> = iolist_to_binary(join(re:split("ccccD","(a){0,3}(?(1)b|(c|))*D",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("ccccD","(a){0,3}(?(1)b|(c|))*D",[]))), - <<"">> = iolist_to_binary(join(re:split("D","(a){0,3}(?(1)b|(c|))*D",[trim]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("ccccD","(a){0,3}(?(1)b|(c|))*D",[]))), + <<"">> = iolist_to_binary(join(re:split("D","(a){0,3}(?(1)b|(c|))*D",[trim]))), <<":::">> = iolist_to_binary(join(re:split("D","(a){0,3}(?(1)b|(c|))*D",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("D","(a){0,3}(?(1)b|(c|))*D",[]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a|)*\\d",[trim]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("D","(a){0,3}(?(1)b|(c|))*D",[]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a|)*\\d",[trim]))), <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a|)*\\d",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a|)*\\d",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(a|)*\\d",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(a|)*\\d",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(a|)*\\d",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(a|)*\\d",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(a|)*\\d",[]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?>a|)*\\d",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(a|)*\\d",[]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?>a|)*\\d",[trim]))), <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?>a|)*\\d",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?>a|)*\\d",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?>a|)*\\d",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?>a|)*\\d",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?>a|)*\\d",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?>a|)*\\d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?>a|)*\\d",[]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?:a|)*\\d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?>a|)*\\d",[]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?:a|)*\\d",[trim]))), <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?:a|)*\\d",[{parts, - 2}]))), - <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?:a|)*\\d",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?:a|)*\\d",[trim]))), + 2}]))), + <<"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","(?:a|)*\\d",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?:a|)*\\d",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?:a|)*\\d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?:a|)*\\d",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","\\Z",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa4","(?:a|)*\\d",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","\\Z",[trim]))), <<"abc:">> = iolist_to_binary(join(re:split("abc","\\Z",[{parts, - 2}]))), - <<"abc:">> = iolist_to_binary(join(re:split("abc","\\Z",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[trim]))), + 2}]))), + <<"abc:">> = iolist_to_binary(join(re:split("abc","\\Z",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","^(?s)(?>.*)(?<!\\n)",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","^(?![^\\n]*\\n\\z)",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[trim]))), <<"abc:">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[{parts, - 2}]))), - <<"abc:">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[trim]))), + 2}]))), + <<"abc:">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[trim]))), <<"abc:">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[{parts, - 2}]))), - <<"abc:">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[]))), - <<"">> = iolist_to_binary(join(re:split("abcd","(.*(.)?)*",[trim]))), + 2}]))), + <<"abc:">> = iolist_to_binary(join(re:split("abc","\\z(?<!\\n)",[]))), + <<"">> = iolist_to_binary(join(re:split("abcd","(.*(.)?)*",[trim]))), <<":::">> = iolist_to_binary(join(re:split("abcd","(.*(.)?)*",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("abcd","(.*(.)?)*",[]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("abcd","(.*(.)?)*",[]))), <<"a:::b:::c:::d">> = iolist_to_binary(join(re:split("abcd","( (A | (?(1)0|) )* )",[extended, - trim]))), + trim]))), <<"a:::bcd">> = iolist_to_binary(join(re:split("abcd","( (A | (?(1)0|) )* )",[extended, {parts, - 2}]))), - <<"a:::b:::c:::d:::">> = iolist_to_binary(join(re:split("abcd","( (A | (?(1)0|) )* )",[extended]))), + 2}]))), + <<"a:::b:::c:::d:::">> = iolist_to_binary(join(re:split("abcd","( (A | (?(1)0|) )* )",[extended]))), <<"a:::b:::c:::d">> = iolist_to_binary(join(re:split("abcd","( ( (?(1)0|) )* )",[extended, - trim]))), + trim]))), <<"a:::bcd">> = iolist_to_binary(join(re:split("abcd","( ( (?(1)0|) )* )",[extended, {parts, - 2}]))), - <<"a:::b:::c:::d:::">> = iolist_to_binary(join(re:split("abcd","( ( (?(1)0|) )* )",[extended]))), + 2}]))), + <<"a:::b:::c:::d:::">> = iolist_to_binary(join(re:split("abcd","( ( (?(1)0|) )* )",[extended]))), <<"a::b::c::d">> = iolist_to_binary(join(re:split("abcd","( (?(1)0|)* )",[extended, - trim]))), + trim]))), <<"a::bcd">> = iolist_to_binary(join(re:split("abcd","( (?(1)0|)* )",[extended, {parts, - 2}]))), - <<"a::b::c::d::">> = iolist_to_binary(join(re:split("abcd","( (?(1)0|)* )",[extended]))), - <<"">> = iolist_to_binary(join(re:split("a]","[[:abcd:xyz]]",[trim]))), + 2}]))), + <<"a::b::c::d::">> = iolist_to_binary(join(re:split("abcd","( (?(1)0|)* )",[extended]))), + <<"">> = iolist_to_binary(join(re:split("a]","[[:abcd:xyz]]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a]","[[:abcd:xyz]]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a]","[[:abcd:xyz]]",[]))), - <<"">> = iolist_to_binary(join(re:split(":]","[[:abcd:xyz]]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a]","[[:abcd:xyz]]",[]))), + <<"">> = iolist_to_binary(join(re:split(":]","[[:abcd:xyz]]",[trim]))), <<":">> = iolist_to_binary(join(re:split(":]","[[:abcd:xyz]]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split(":]","[[:abcd:xyz]]",[]))), - <<"">> = iolist_to_binary(join(re:split("a","[abc[:x\\]pqr]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split(":]","[[:abcd:xyz]]",[]))), + <<"">> = iolist_to_binary(join(re:split("a","[abc[:x\\]pqr]",[trim]))), <<":">> = iolist_to_binary(join(re:split("a","[abc[:x\\]pqr]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("a","[abc[:x\\]pqr]",[]))), - <<"">> = iolist_to_binary(join(re:split("[","[abc[:x\\]pqr]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("a","[abc[:x\\]pqr]",[]))), + <<"">> = iolist_to_binary(join(re:split("[","[abc[:x\\]pqr]",[trim]))), <<":">> = iolist_to_binary(join(re:split("[","[abc[:x\\]pqr]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("[","[abc[:x\\]pqr]",[]))), - <<"">> = iolist_to_binary(join(re:split(":","[abc[:x\\]pqr]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("[","[abc[:x\\]pqr]",[]))), + <<"">> = iolist_to_binary(join(re:split(":","[abc[:x\\]pqr]",[trim]))), <<":">> = iolist_to_binary(join(re:split(":","[abc[:x\\]pqr]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split(":","[abc[:x\\]pqr]",[]))), - <<"">> = iolist_to_binary(join(re:split("]","[abc[:x\\]pqr]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split(":","[abc[:x\\]pqr]",[]))), + <<"">> = iolist_to_binary(join(re:split("]","[abc[:x\\]pqr]",[trim]))), <<":">> = iolist_to_binary(join(re:split("]","[abc[:x\\]pqr]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("]","[abc[:x\\]pqr]",[]))), - <<"">> = iolist_to_binary(join(re:split("p","[abc[:x\\]pqr]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("]","[abc[:x\\]pqr]",[]))), + <<"">> = iolist_to_binary(join(re:split("p","[abc[:x\\]pqr]",[trim]))), <<":">> = iolist_to_binary(join(re:split("p","[abc[:x\\]pqr]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("p","[abc[:x\\]pqr]",[]))), - <<"fooabcfoo">> = iolist_to_binary(join(re:split("fooabcfoo",".*[op][xyz]",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("p","[abc[:x\\]pqr]",[]))), + <<"fooabcfoo">> = iolist_to_binary(join(re:split("fooabcfoo",".*[op][xyz]",[trim]))), <<"fooabcfoo">> = iolist_to_binary(join(re:split("fooabcfoo",".*[op][xyz]",[{parts, - 2}]))), - <<"fooabcfoo">> = iolist_to_binary(join(re:split("fooabcfoo",".*[op][xyz]",[]))), - <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)",[trim]))), + 2}]))), + <<"fooabcfoo">> = iolist_to_binary(join(re:split("fooabcfoo",".*[op][xyz]",[]))), + <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)",[trim]))), <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)",[{parts, - 2}]))), - <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)",[]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)",[trim]))), + 2}]))), + <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)",[]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)",[trim]))), <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)",[{parts, - 2}]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)",[]))), - <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=^.*b)b|^)",[trim]))), + 2}]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)",[]))), + <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=^.*b)b|^)",[trim]))), <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=^.*b)b|^)",[{parts, - 2}]))), - <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=^.*b)b|^)",[]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(?(?=^.*b)b|^)",[trim]))), + 2}]))), + <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=^.*b)b|^)",[]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(?(?=^.*b)b|^)",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(?(?=^.*b)b|^)",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(?(?=^.*b)b|^)",[]))), - <<"a:d:c">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)*",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(?(?=^.*b)b|^)",[]))), + <<"a:d:c">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)*",[trim]))), <<"a:dc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)*",[{parts, - 2}]))), - <<"a:d:c:">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)*",[]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)*",[trim]))), + 2}]))), + <<"a:d:c:">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)*",[]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)*",[trim]))), <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)*",[{parts, - 2}]))), - <<"a:c:">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)*",[]))), + 2}]))), + <<"a:c:">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)*",[]))), ok. run37() -> - <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)+",[trim]))), + <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)+",[trim]))), <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)+",[{parts, - 2}]))), - <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)+",[]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)+",[trim]))), + 2}]))), + <<"adc">> = iolist_to_binary(join(re:split("adc","(?(?=.*b)b|^)+",[]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)+",[trim]))), <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)+",[{parts, - 2}]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)+",[]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=b).*b|^d)",[trim]))), + 2}]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b)b|^)+",[]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=b).*b|^d)",[trim]))), <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=b).*b|^d)",[{parts, - 2}]))), - <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=b).*b|^d)",[]))), - <<":c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b).*b|^d)",[trim]))), + 2}]))), + <<"a:c">> = iolist_to_binary(join(re:split("abc","(?(?=b).*b|^d)",[]))), + <<":c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b).*b|^d)",[trim]))), <<":c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b).*b|^d)",[{parts, - 2}]))), - <<":c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b).*b|^d)",[]))), - <<"">> = iolist_to_binary(join(re:split("%ab%","^%((?(?=[a])[^%])|b)*%$",[trim]))), + 2}]))), + <<":c">> = iolist_to_binary(join(re:split("abc","(?(?=.*b).*b|^d)",[]))), + <<"">> = iolist_to_binary(join(re:split("%ab%","^%((?(?=[a])[^%])|b)*%$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("%ab%","^%((?(?=[a])[^%])|b)*%$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("%ab%","^%((?(?=[a])[^%])|b)*%$",[]))), - <<"X:X">> = iolist_to_binary(join(re:split("XabX","(?i)a(?-i)b|c",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("%ab%","^%((?(?=[a])[^%])|b)*%$",[]))), + <<"X:X">> = iolist_to_binary(join(re:split("XabX","(?i)a(?-i)b|c",[trim]))), <<"X:X">> = iolist_to_binary(join(re:split("XabX","(?i)a(?-i)b|c",[{parts, - 2}]))), - <<"X:X">> = iolist_to_binary(join(re:split("XabX","(?i)a(?-i)b|c",[]))), - <<"X:X">> = iolist_to_binary(join(re:split("XAbX","(?i)a(?-i)b|c",[trim]))), + 2}]))), + <<"X:X">> = iolist_to_binary(join(re:split("XabX","(?i)a(?-i)b|c",[]))), + <<"X:X">> = iolist_to_binary(join(re:split("XAbX","(?i)a(?-i)b|c",[trim]))), <<"X:X">> = iolist_to_binary(join(re:split("XAbX","(?i)a(?-i)b|c",[{parts, - 2}]))), - <<"X:X">> = iolist_to_binary(join(re:split("XAbX","(?i)a(?-i)b|c",[]))), - <<"C:C">> = iolist_to_binary(join(re:split("CcC","(?i)a(?-i)b|c",[trim]))), + 2}]))), + <<"X:X">> = iolist_to_binary(join(re:split("XAbX","(?i)a(?-i)b|c",[]))), + <<"C:C">> = iolist_to_binary(join(re:split("CcC","(?i)a(?-i)b|c",[trim]))), <<"C:C">> = iolist_to_binary(join(re:split("CcC","(?i)a(?-i)b|c",[{parts, - 2}]))), - <<"C:C">> = iolist_to_binary(join(re:split("CcC","(?i)a(?-i)b|c",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?i)a(?-i)b|c",[trim]))), + 2}]))), + <<"C:C">> = iolist_to_binary(join(re:split("CcC","(?i)a(?-i)b|c",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?i)a(?-i)b|c",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?i)a(?-i)b|c",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?i)a(?-i)b|c",[]))), - <<"XABX">> = iolist_to_binary(join(re:split("XABX","(?i)a(?-i)b|c",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?i)a(?-i)b|c",[]))), + <<"XABX">> = iolist_to_binary(join(re:split("XABX","(?i)a(?-i)b|c",[trim]))), <<"XABX">> = iolist_to_binary(join(re:split("XABX","(?i)a(?-i)b|c",[{parts, - 2}]))), - <<"XABX">> = iolist_to_binary(join(re:split("XABX","(?i)a(?-i)b|c",[]))), + 2}]))), + <<"XABX">> = iolist_to_binary(join(re:split("XABX","(?i)a(?-i)b|c",[]))), <<"">> = iolist_to_binary(join(re:split(" -
","[\\x00-\\xff\\s]+",[trim]))), +
","[\\x00-\\xff\\s]+",[trim]))), <<":">> = iolist_to_binary(join(re:split(" -
","[\\x00-\\xff\\s]+",[{parts,2}]))), +
","[\\x00-\\xff\\s]+",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split(" -
","[\\x00-\\xff\\s]+",[]))), +
","[\\x00-\\xff\\s]+",[]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[caseless, - trim]))), + trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[caseless, {parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[caseless]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[trim]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[caseless]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[trim]))), <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[{parts, - 2}]))), - <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[]))), + 2}]))), + <<"abc">> = iolist_to_binary(join(re:split("abc","(abc)\\1",[]))), <<":a">> = iolist_to_binary(join(re:split("12abc","[^a]*",[caseless, - trim]))), + trim]))), <<":abc">> = iolist_to_binary(join(re:split("12abc","[^a]*",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("12abc","[^a]*",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("12abc","[^a]*",[caseless]))), <<":A">> = iolist_to_binary(join(re:split("12ABC","[^a]*",[caseless, - trim]))), + trim]))), <<":ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]*",[caseless, {parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("12ABC","[^a]*",[caseless]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("12ABC","[^a]*",[caseless]))), <<":a">> = iolist_to_binary(join(re:split("12abc","[^a]*+",[caseless, - trim]))), + trim]))), <<":abc">> = iolist_to_binary(join(re:split("12abc","[^a]*+",[caseless, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("12abc","[^a]*+",[caseless]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("12abc","[^a]*+",[caseless]))), <<":A">> = iolist_to_binary(join(re:split("12ABC","[^a]*+",[caseless, - trim]))), + trim]))), <<":ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]*+",[caseless, {parts, - 2}]))), - <<":A:">> = iolist_to_binary(join(re:split("12ABC","[^a]*+",[caseless]))), + 2}]))), + <<":A:">> = iolist_to_binary(join(re:split("12ABC","[^a]*+",[caseless]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]*?X",[caseless, - trim]))), + trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]*?X",[caseless, {parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]*?X",[caseless]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]*?X",[caseless]))), <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]*?X",[caseless, - trim]))), + trim]))), <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]*?X",[caseless, {parts, - 2}]))), - <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]*?X",[caseless]))), + 2}]))), + <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]*?X",[caseless]))), <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]*?X",[caseless, - trim]))), + trim]))), <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]*?X",[caseless, {parts, - 2}]))), - <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]*?X",[caseless]))), + 2}]))), + <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]*?X",[caseless]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]+?X",[caseless, - trim]))), + trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]+?X",[caseless, {parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]+?X",[caseless]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","[^a]+?X",[caseless]))), <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]+?X",[caseless, - trim]))), + trim]))), <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]+?X",[caseless, {parts, - 2}]))), - <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]+?X",[caseless]))), + 2}]))), + <<"12abc">> = iolist_to_binary(join(re:split("12abc","[^a]+?X",[caseless]))), <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]+?X",[caseless, - trim]))), + trim]))), <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]+?X",[caseless, {parts, - 2}]))), - <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]+?X",[caseless]))), + 2}]))), + <<"12ABC">> = iolist_to_binary(join(re:split("12ABC","[^a]+?X",[caseless]))), <<"12a:b">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?X",[caseless, - trim]))), + trim]))), <<"12a:bcX">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?X",[caseless, {parts, - 2}]))), - <<"12a:b:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?X",[caseless]))), + 2}]))), + <<"12a:b:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?X",[caseless]))), <<"12A:B">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?X",[caseless, - trim]))), + trim]))), <<"12A:BCX">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?X",[caseless, {parts, - 2}]))), - <<"12A:B:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?X",[caseless]))), + 2}]))), + <<"12A:B:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?X",[caseless]))), <<"B">> = iolist_to_binary(join(re:split("BCX","[^a]?X",[caseless, - trim]))), + trim]))), <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]?X",[caseless, {parts, - 2}]))), - <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]?X",[caseless]))), + 2}]))), + <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]?X",[caseless]))), <<"12a:b">> = iolist_to_binary(join(re:split("12aXbcX","[^a]??X",[caseless, - trim]))), + trim]))), <<"12a:bcX">> = iolist_to_binary(join(re:split("12aXbcX","[^a]??X",[caseless, {parts, - 2}]))), - <<"12a:b:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]??X",[caseless]))), + 2}]))), + <<"12a:b:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]??X",[caseless]))), <<"12A:B">> = iolist_to_binary(join(re:split("12AXBCX","[^a]??X",[caseless, - trim]))), + trim]))), <<"12A:BCX">> = iolist_to_binary(join(re:split("12AXBCX","[^a]??X",[caseless, {parts, - 2}]))), - <<"12A:B:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]??X",[caseless]))), + 2}]))), + <<"12A:B:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]??X",[caseless]))), <<"B">> = iolist_to_binary(join(re:split("BCX","[^a]??X",[caseless, - trim]))), + trim]))), <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]??X",[caseless, {parts, - 2}]))), - <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]??X",[caseless]))), + 2}]))), + <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]??X",[caseless]))), <<"12aXb">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?+X",[caseless, - trim]))), + trim]))), <<"12aXb:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?+X",[caseless, {parts, - 2}]))), - <<"12aXb:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?+X",[caseless]))), + 2}]))), + <<"12aXb:">> = iolist_to_binary(join(re:split("12aXbcX","[^a]?+X",[caseless]))), <<"12AXB">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?+X",[caseless, - trim]))), + trim]))), <<"12AXB:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?+X",[caseless, {parts, - 2}]))), - <<"12AXB:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?+X",[caseless]))), + 2}]))), + <<"12AXB:">> = iolist_to_binary(join(re:split("12AXBCX","[^a]?+X",[caseless]))), <<"B">> = iolist_to_binary(join(re:split("BCX","[^a]?+X",[caseless, - trim]))), + trim]))), <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]?+X",[caseless, {parts, - 2}]))), - <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]?+X",[caseless]))), + 2}]))), + <<"B:">> = iolist_to_binary(join(re:split("BCX","[^a]?+X",[caseless]))), <<"a">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}",[caseless, - trim]))), + trim]))), <<"a:ef">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}",[caseless, {parts, - 2}]))), - <<"a::">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}",[caseless]))), + 2}]))), + <<"a::">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}",[caseless]))), <<"A">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}",[caseless, - trim]))), + trim]))), <<"A:EF">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}",[caseless, {parts, - 2}]))), - <<"A::">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}",[caseless]))), + 2}]))), + <<"A::">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}",[caseless]))), <<"a::f">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}?",[caseless, - trim]))), + trim]))), <<"a:def">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}?",[caseless, {parts, - 2}]))), - <<"a::f">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}?",[caseless]))), + 2}]))), + <<"a::f">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}?",[caseless]))), <<"A::F">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}?",[caseless, - trim]))), + trim]))), <<"A:DEF">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}?",[caseless, {parts, - 2}]))), - <<"A::F">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}?",[caseless]))), + 2}]))), + <<"A::F">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}?",[caseless]))), <<"a">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}+",[caseless, - trim]))), + trim]))), <<"a:ef">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}+",[caseless, {parts, - 2}]))), - <<"a::">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}+",[caseless]))), + 2}]))), + <<"a::">> = iolist_to_binary(join(re:split("abcdef","[^a]{2,3}+",[caseless]))), <<"A">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}+",[caseless, - trim]))), + trim]))), <<"A:EF">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}+",[caseless, {parts, - 2}]))), - <<"A::">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}+",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("Z","((a|)+)+Z",[trim]))), + 2}]))), + <<"A::">> = iolist_to_binary(join(re:split("ABCDEF","[^a]{2,3}+",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("Z","((a|)+)+Z",[trim]))), <<":::">> = iolist_to_binary(join(re:split("Z","((a|)+)+Z",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("Z","((a|)+)+Z",[]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("Z","((a|)+)+Z",[]))), ok. run38() -> - <<"::a">> = iolist_to_binary(join(re:split("ac","(a)b|(a)c",[trim]))), + <<"::a">> = iolist_to_binary(join(re:split("ac","(a)b|(a)c",[trim]))), <<"::a:">> = iolist_to_binary(join(re:split("ac","(a)b|(a)c",[{parts, - 2}]))), - <<"::a:">> = iolist_to_binary(join(re:split("ac","(a)b|(a)c",[]))), - <<"::a">> = iolist_to_binary(join(re:split("ac","(?>(a))b|(a)c",[trim]))), + 2}]))), + <<"::a:">> = iolist_to_binary(join(re:split("ac","(a)b|(a)c",[]))), + <<"::a">> = iolist_to_binary(join(re:split("ac","(?>(a))b|(a)c",[trim]))), <<"::a:">> = iolist_to_binary(join(re:split("ac","(?>(a))b|(a)c",[{parts, - 2}]))), - <<"::a:">> = iolist_to_binary(join(re:split("ac","(?>(a))b|(a)c",[]))), - <<"::a">> = iolist_to_binary(join(re:split("ac","(?=(a))ab|(a)c",[trim]))), + 2}]))), + <<"::a:">> = iolist_to_binary(join(re:split("ac","(?>(a))b|(a)c",[]))), + <<"::a">> = iolist_to_binary(join(re:split("ac","(?=(a))ab|(a)c",[trim]))), <<"::a:">> = iolist_to_binary(join(re:split("ac","(?=(a))ab|(a)c",[{parts, - 2}]))), - <<"::a:">> = iolist_to_binary(join(re:split("ac","(?=(a))ab|(a)c",[]))), - <<":ac::a">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)",[trim]))), + 2}]))), + <<"::a:">> = iolist_to_binary(join(re:split("ac","(?=(a))ab|(a)c",[]))), + <<":ac::a">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)",[trim]))), <<":ac::a:">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)",[{parts, - 2}]))), - <<":ac::a:">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)",[]))), - <<":ac::a">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)++",[trim]))), + 2}]))), + <<":ac::a:">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)",[]))), + <<":ac::a">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)++",[trim]))), <<":ac::a:">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)++",[{parts, - 2}]))), - <<":ac::a:">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)++",[]))), - <<"::a">> = iolist_to_binary(join(re:split("ac","(?:(?>(a))b|(a)c)++",[trim]))), + 2}]))), + <<":ac::a:">> = iolist_to_binary(join(re:split("ac","((?>(a))b|(a)c)++",[]))), + <<"::a">> = iolist_to_binary(join(re:split("ac","(?:(?>(a))b|(a)c)++",[trim]))), <<"::a:">> = iolist_to_binary(join(re:split("ac","(?:(?>(a))b|(a)c)++",[{parts, - 2}]))), - <<"::a:">> = iolist_to_binary(join(re:split("ac","(?:(?>(a))b|(a)c)++",[]))), - <<"::a:ac">> = iolist_to_binary(join(re:split("ac","(?=(?>(a))b|(a)c)(..)",[trim]))), + 2}]))), + <<"::a:">> = iolist_to_binary(join(re:split("ac","(?:(?>(a))b|(a)c)++",[]))), + <<"::a:ac">> = iolist_to_binary(join(re:split("ac","(?=(?>(a))b|(a)c)(..)",[trim]))), <<"::a:ac:">> = iolist_to_binary(join(re:split("ac","(?=(?>(a))b|(a)c)(..)",[{parts, - 2}]))), - <<"::a:ac:">> = iolist_to_binary(join(re:split("ac","(?=(?>(a))b|(a)c)(..)",[]))), - <<"::a">> = iolist_to_binary(join(re:split("ac","(?>(?>(a))b|(a)c)",[trim]))), + 2}]))), + <<"::a:ac:">> = iolist_to_binary(join(re:split("ac","(?=(?>(a))b|(a)c)(..)",[]))), + <<"::a">> = iolist_to_binary(join(re:split("ac","(?>(?>(a))b|(a)c)",[trim]))), <<"::a:">> = iolist_to_binary(join(re:split("ac","(?>(?>(a))b|(a)c)",[{parts, - 2}]))), - <<"::a:">> = iolist_to_binary(join(re:split("ac","(?>(?>(a))b|(a)c)",[]))), - <<":aaaabaaabaabab:aaa:aabab">> = iolist_to_binary(join(re:split("aaaabaaabaabab","((?>(a+)b)+(aabab))",[trim]))), + 2}]))), + <<"::a:">> = iolist_to_binary(join(re:split("ac","(?>(?>(a))b|(a)c)",[]))), + <<":aaaabaaabaabab:aaa:aabab">> = iolist_to_binary(join(re:split("aaaabaaabaabab","((?>(a+)b)+(aabab))",[trim]))), <<":aaaabaaabaabab:aaa:aabab:">> = iolist_to_binary(join(re:split("aaaabaaabaabab","((?>(a+)b)+(aabab))",[{parts, - 2}]))), - <<":aaaabaaabaabab:aaa:aabab:">> = iolist_to_binary(join(re:split("aaaabaaabaabab","((?>(a+)b)+(aabab))",[]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+?c",[trim]))), + 2}]))), + <<":aaaabaaabaabab:aaa:aabab:">> = iolist_to_binary(join(re:split("aaaabaaabaabab","((?>(a+)b)+(aabab))",[]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+?c",[trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+?c",[{parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+?c",[]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+c",[trim]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+?c",[]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+c",[trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+c",[{parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aabc","(?:a+|ab)+c",[trim]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","(?>a+|ab)+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aabc","(?:a+|ab)+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","(?:a+|ab)+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","(?:a+|ab)+c",[]))), - <<":a">> = iolist_to_binary(join(re:split("a","(?(?=(a))a)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","(?:a+|ab)+c",[]))), + <<":a">> = iolist_to_binary(join(re:split("a","(?(?=(a))a)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("a","(?(?=(a))a)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("a","(?(?=(a))a)",[]))), - <<":a:b">> = iolist_to_binary(join(re:split("ab","(?(?=(a))a)(b)",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("a","(?(?=(a))a)",[]))), + <<":a:b">> = iolist_to_binary(join(re:split("ab","(?(?=(a))a)(b)",[trim]))), <<":a:b:">> = iolist_to_binary(join(re:split("ab","(?(?=(a))a)(b)",[{parts, - 2}]))), - <<":a:b:">> = iolist_to_binary(join(re:split("ab","(?(?=(a))a)(b)",[]))), - <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)++c",[trim]))), + 2}]))), + <<":a:b:">> = iolist_to_binary(join(re:split("ab","(?(?=(a))a)(b)",[]))), + <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)++c",[trim]))), <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)++c",[{parts, - 2}]))), - <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)++c",[]))), - <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?>a|ab)++c",[trim]))), + 2}]))), + <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)++c",[]))), + <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?>a|ab)++c",[trim]))), <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?>a|ab)++c",[{parts, - 2}]))), - <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?>a|ab)++c",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)+c",[trim]))), + 2}]))), + <<"aaaabc">> = iolist_to_binary(join(re:split("aaaabc","^(?>a|ab)++c",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)+c",[]))), - <<"">> = iolist_to_binary(join(re:split("xyz","(?=abc){0}xyz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaabc","^(?:a|ab)+c",[]))), + <<"">> = iolist_to_binary(join(re:split("xyz","(?=abc){0}xyz",[trim]))), <<":">> = iolist_to_binary(join(re:split("xyz","(?=abc){0}xyz",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("xyz","(?=abc){0}xyz",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?=abc){1}xyz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("xyz","(?=abc){0}xyz",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?=abc){1}xyz",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?=abc){1}xyz",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?=abc){1}xyz",[]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","(?=abc){1}xyz",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?=abc){1}xyz",[]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","(?=abc){1}xyz",[trim]))), <<"xyz">> = iolist_to_binary(join(re:split("xyz","(?=abc){1}xyz",[{parts, - 2}]))), - <<"xyz">> = iolist_to_binary(join(re:split("xyz","(?=abc){1}xyz",[]))), - <<":a">> = iolist_to_binary(join(re:split("ab","(?=(a))?.",[trim]))), + 2}]))), + <<"xyz">> = iolist_to_binary(join(re:split("xyz","(?=abc){1}xyz",[]))), + <<":a">> = iolist_to_binary(join(re:split("ab","(?=(a))?.",[trim]))), <<":a:b">> = iolist_to_binary(join(re:split("ab","(?=(a))?.",[{parts, - 2}]))), - <<":a:::">> = iolist_to_binary(join(re:split("ab","(?=(a))?.",[]))), - <<"">> = iolist_to_binary(join(re:split("bc","(?=(a))?.",[trim]))), + 2}]))), + <<":a:::">> = iolist_to_binary(join(re:split("ab","(?=(a))?.",[]))), + <<"">> = iolist_to_binary(join(re:split("bc","(?=(a))?.",[trim]))), <<"::c">> = iolist_to_binary(join(re:split("bc","(?=(a))?.",[{parts, - 2}]))), - <<"::::">> = iolist_to_binary(join(re:split("bc","(?=(a))?.",[]))), + 2}]))), + <<"::::">> = iolist_to_binary(join(re:split("bc","(?=(a))?.",[]))), ok. run39() -> - <<"">> = iolist_to_binary(join(re:split("ab","(?=(a))??.",[trim]))), + <<"">> = iolist_to_binary(join(re:split("ab","(?=(a))??.",[trim]))), <<"::b">> = iolist_to_binary(join(re:split("ab","(?=(a))??.",[{parts, - 2}]))), - <<"::::">> = iolist_to_binary(join(re:split("ab","(?=(a))??.",[]))), - <<"">> = iolist_to_binary(join(re:split("bc","(?=(a))??.",[trim]))), + 2}]))), + <<"::::">> = iolist_to_binary(join(re:split("ab","(?=(a))??.",[]))), + <<"">> = iolist_to_binary(join(re:split("bc","(?=(a))??.",[trim]))), <<"::c">> = iolist_to_binary(join(re:split("bc","(?=(a))??.",[{parts, - 2}]))), - <<"::::">> = iolist_to_binary(join(re:split("bc","(?=(a))??.",[]))), - <<":b">> = iolist_to_binary(join(re:split("abd","^(?=(?1))?[az]([abc])d",[trim]))), + 2}]))), + <<"::::">> = iolist_to_binary(join(re:split("bc","(?=(a))??.",[]))), + <<":b">> = iolist_to_binary(join(re:split("abd","^(?=(?1))?[az]([abc])d",[trim]))), <<":b:">> = iolist_to_binary(join(re:split("abd","^(?=(?1))?[az]([abc])d",[{parts, - 2}]))), - <<":b:">> = iolist_to_binary(join(re:split("abd","^(?=(?1))?[az]([abc])d",[]))), - <<":c:xx">> = iolist_to_binary(join(re:split("zcdxx","^(?=(?1))?[az]([abc])d",[trim]))), + 2}]))), + <<":b:">> = iolist_to_binary(join(re:split("abd","^(?=(?1))?[az]([abc])d",[]))), + <<":c:xx">> = iolist_to_binary(join(re:split("zcdxx","^(?=(?1))?[az]([abc])d",[trim]))), <<":c:xx">> = iolist_to_binary(join(re:split("zcdxx","^(?=(?1))?[az]([abc])d",[{parts, - 2}]))), - <<":c:xx">> = iolist_to_binary(join(re:split("zcdxx","^(?=(?1))?[az]([abc])d",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaa","^(?!a){0}\\w+",[trim]))), + 2}]))), + <<":c:xx">> = iolist_to_binary(join(re:split("zcdxx","^(?=(?1))?[az]([abc])d",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaa","^(?!a){0}\\w+",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaa","^(?!a){0}\\w+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaa","^(?!a){0}\\w+",[]))), - <<"abc:abc">> = iolist_to_binary(join(re:split("abcxyz","(?<=(abc))?xyz",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaa","^(?!a){0}\\w+",[]))), + <<"abc:abc">> = iolist_to_binary(join(re:split("abcxyz","(?<=(abc))?xyz",[trim]))), <<"abc:abc:">> = iolist_to_binary(join(re:split("abcxyz","(?<=(abc))?xyz",[{parts, - 2}]))), - <<"abc:abc:">> = iolist_to_binary(join(re:split("abcxyz","(?<=(abc))?xyz",[]))), - <<"pqr">> = iolist_to_binary(join(re:split("pqrxyz","(?<=(abc))?xyz",[trim]))), + 2}]))), + <<"abc:abc:">> = iolist_to_binary(join(re:split("abcxyz","(?<=(abc))?xyz",[]))), + <<"pqr">> = iolist_to_binary(join(re:split("pqrxyz","(?<=(abc))?xyz",[trim]))), <<"pqr::">> = iolist_to_binary(join(re:split("pqrxyz","(?<=(abc))?xyz",[{parts, - 2}]))), - <<"pqr::">> = iolist_to_binary(join(re:split("pqrxyz","(?<=(abc))?xyz",[]))), - <<"">> = iolist_to_binary(join(re:split("ggg<<<aaa>>>","^[\\g<a>]+",[trim]))), + 2}]))), + <<"pqr::">> = iolist_to_binary(join(re:split("pqrxyz","(?<=(abc))?xyz",[]))), + <<"">> = iolist_to_binary(join(re:split("ggg<<<aaa>>>","^[\\g<a>]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("ggg<<<aaa>>>","^[\\g<a>]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ggg<<<aaa>>>","^[\\g<a>]+",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\g<a>]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ggg<<<aaa>>>","^[\\g<a>]+",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\g<a>]+",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\g<a>]+",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\g<a>]+",[]))), - <<"\\ga">> = iolist_to_binary(join(re:split("\\ga","^[\\g<a>]+",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^[\\g<a>]+",[]))), + <<"\\ga">> = iolist_to_binary(join(re:split("\\ga","^[\\g<a>]+",[trim]))), <<"\\ga">> = iolist_to_binary(join(re:split("\\ga","^[\\g<a>]+",[{parts, - 2}]))), - <<"\\ga">> = iolist_to_binary(join(re:split("\\ga","^[\\g<a>]+",[]))), - <<":xyz">> = iolist_to_binary(join(re:split("gggagagaxyz","^[\\ga]+",[trim]))), + 2}]))), + <<"\\ga">> = iolist_to_binary(join(re:split("\\ga","^[\\g<a>]+",[]))), + <<":xyz">> = iolist_to_binary(join(re:split("gggagagaxyz","^[\\ga]+",[trim]))), <<":xyz">> = iolist_to_binary(join(re:split("gggagagaxyz","^[\\ga]+",[{parts, - 2}]))), - <<":xyz">> = iolist_to_binary(join(re:split("gggagagaxyz","^[\\ga]+",[]))), - <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::Z","^[:a[:digit:]]+",[trim]))), + 2}]))), + <<":xyz">> = iolist_to_binary(join(re:split("gggagagaxyz","^[\\ga]+",[]))), + <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::Z","^[:a[:digit:]]+",[trim]))), <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::Z","^[:a[:digit:]]+",[{parts, - 2}]))), - <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::Z","^[:a[:digit:]]+",[]))), - <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::bbbZ","^[:a[:digit:]:b]+",[trim]))), + 2}]))), + <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::Z","^[:a[:digit:]]+",[]))), + <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::bbbZ","^[:a[:digit:]:b]+",[trim]))), <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::bbbZ","^[:a[:digit:]:b]+",[{parts, - 2}]))), - <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::bbbZ","^[:a[:digit:]:b]+",[]))), - <<"">> = iolist_to_binary(join(re:split(":xxx:","[:a]xxx[b:]",[trim]))), + 2}]))), + <<":Z">> = iolist_to_binary(join(re:split("aaaa444:::bbbZ","^[:a[:digit:]:b]+",[]))), + <<"">> = iolist_to_binary(join(re:split(":xxx:","[:a]xxx[b:]",[trim]))), <<":">> = iolist_to_binary(join(re:split(":xxx:","[:a]xxx[b:]",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split(":xxx:","[:a]xxx[b:]",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split(":xxx:","[:a]xxx[b:]",[]))), <<"xaa:c">> = iolist_to_binary(join(re:split("xaabc","(?<=a{2})b",[caseless, - trim]))), + trim]))), <<"xaa:c">> = iolist_to_binary(join(re:split("xaabc","(?<=a{2})b",[caseless, {parts, - 2}]))), - <<"xaa:c">> = iolist_to_binary(join(re:split("xaabc","(?<=a{2})b",[caseless]))), + 2}]))), + <<"xaa:c">> = iolist_to_binary(join(re:split("xaabc","(?<=a{2})b",[caseless]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=a{2})b",[caseless, - trim]))), + trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=a{2})b",[caseless, {parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=a{2})b",[caseless]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=a{2})b",[caseless]))), <<"xabc">> = iolist_to_binary(join(re:split("xabc","(?<=a{2})b",[caseless, - trim]))), + trim]))), <<"xabc">> = iolist_to_binary(join(re:split("xabc","(?<=a{2})b",[caseless, {parts, - 2}]))), - <<"xabc">> = iolist_to_binary(join(re:split("xabc","(?<=a{2})b",[caseless]))), + 2}]))), + <<"xabc">> = iolist_to_binary(join(re:split("xabc","(?<=a{2})b",[caseless]))), <<"xa:c">> = iolist_to_binary(join(re:split("xabc","(?<!a{2})b",[caseless, - trim]))), + trim]))), <<"xa:c">> = iolist_to_binary(join(re:split("xabc","(?<!a{2})b",[caseless, {parts, - 2}]))), - <<"xa:c">> = iolist_to_binary(join(re:split("xabc","(?<!a{2})b",[caseless]))), + 2}]))), + <<"xa:c">> = iolist_to_binary(join(re:split("xabc","(?<!a{2})b",[caseless]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<!a{2})b",[caseless, - trim]))), + trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<!a{2})b",[caseless, {parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<!a{2})b",[caseless]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<!a{2})b",[caseless]))), <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<!a{2})b",[caseless, - trim]))), + trim]))), <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<!a{2})b",[caseless, {parts, - 2}]))), - <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<!a{2})b",[caseless]))), - <<"xa ">> = iolist_to_binary(join(re:split("xa c","(?<=a\\h)c",[trim]))), + 2}]))), + <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<!a{2})b",[caseless]))), + <<"xa ">> = iolist_to_binary(join(re:split("xa c","(?<=a\\h)c",[trim]))), <<"xa :">> = iolist_to_binary(join(re:split("xa c","(?<=a\\h)c",[{parts, - 2}]))), - <<"xa :">> = iolist_to_binary(join(re:split("xa c","(?<=a\\h)c",[]))), - <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[trim]))), + 2}]))), + <<"xa :">> = iolist_to_binary(join(re:split("xa c","(?<=a\\h)c",[]))), + <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[trim]))), <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[{parts, - 2}]))), - <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[]))), - <<"aAA:c">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[trim]))), + 2}]))), + <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[]))), + <<"aAA:c">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[trim]))), <<"aAA:c">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[{parts, - 2}]))), - <<"aAA:c">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[trim]))), + 2}]))), + <<"aAA:c">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[]))), - <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[]))), + <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[trim]))), <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[{parts, - 2}]))), - <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[]))), + 2}]))), + <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[]))), <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[caseless, - trim]))), + trim]))), <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[caseless, {parts, - 2}]))), - <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[caseless]))), + 2}]))), + <<"axx:c">> = iolist_to_binary(join(re:split("axxbc","(?<=[^a]{2})b",[caseless]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[caseless, - trim]))), + trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[caseless, {parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[caseless]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=[^a]{2})b",[caseless]))), <<"aAAbc">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[caseless, - trim]))), + trim]))), <<"aAAbc">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[caseless, {parts, - 2}]))), - <<"aAAbc">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[caseless]))), + 2}]))), + <<"aAAbc">> = iolist_to_binary(join(re:split("aAAbc","(?<=[^a]{2})b",[caseless]))), <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[caseless, - trim]))), + trim]))), <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[caseless, {parts, - 2}]))), - <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[caseless]))), - <<"ab">> = iolist_to_binary(join(re:split("abc","(?<=a\\H)c",[trim]))), + 2}]))), + <<"xaabc">> = iolist_to_binary(join(re:split("xaabc","(?<=[^a]{2})b",[caseless]))), + <<"ab">> = iolist_to_binary(join(re:split("abc","(?<=a\\H)c",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abc","(?<=a\\H)c",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abc","(?<=a\\H)c",[]))), - <<"ab">> = iolist_to_binary(join(re:split("abc","(?<=a\\V)c",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abc","(?<=a\\H)c",[]))), + <<"ab">> = iolist_to_binary(join(re:split("abc","(?<=a\\V)c",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abc","(?<=a\\V)c",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abc","(?<=a\\V)c",[]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abc","(?<=a\\V)c",[]))), <<"a ">> = iolist_to_binary(join(re:split("a -c","(?<=a\\v)c",[trim]))), +c","(?<=a\\v)c",[trim]))), <<"a :">> = iolist_to_binary(join(re:split("a -c","(?<=a\\v)c",[{parts,2}]))), +c","(?<=a\\v)c",[{parts,2}]))), <<"a :">> = iolist_to_binary(join(re:split("a -c","(?<=a\\v)c",[]))), - <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)++Y",[trim]))), +c","(?<=a\\v)c",[]))), + <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)++Y",[trim]))), <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)++Y",[{parts, - 2}]))), - <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)++Y",[]))), - <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)*+Y",[trim]))), + 2}]))), + <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)++Y",[]))), + <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)*+Y",[trim]))), <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)*+Y",[{parts, - 2}]))), - <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)*+Y",[]))), - <<":aaa">> = iolist_to_binary(join(re:split("aaaaaaa","^(a{2,3}){2,}+a",[trim]))), + 2}]))), + <<"X:X">> = iolist_to_binary(join(re:split("XcccddYX","(?(?=c)c|d)*+Y",[]))), + <<":aaa">> = iolist_to_binary(join(re:split("aaaaaaa","^(a{2,3}){2,}+a",[trim]))), <<":aaa:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a{2,3}){2,}+a",[{parts, - 2}]))), - <<":aaa:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a{2,3}){2,}+a",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3}){2,}+a",[trim]))), + 2}]))), + <<":aaa:">> = iolist_to_binary(join(re:split("aaaaaaa","^(a{2,3}){2,}+a",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3}){2,}+a",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3}){2,}+a",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3}){2,}+a",[]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3}){2,}+a",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3}){2,}+a",[]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3}){2,}+a",[trim]))), <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3}){2,}+a",[{parts, - 2}]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3}){2,}+a",[]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a{2,3}){2,}+a",[trim]))), + 2}]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3}){2,}+a",[]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a{2,3}){2,}+a",[trim]))), <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a{2,3}){2,}+a",[{parts, - 2}]))), - <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a{2,3}){2,}+a",[]))), + 2}]))), + <<"aaaaaaaaa">> = iolist_to_binary(join(re:split("aaaaaaaaa","^(a{2,3}){2,}+a",[]))), ok. run40() -> - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})++a",[trim]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})++a",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})++a",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})++a",[]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})++a",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})++a",[]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})++a",[trim]))), <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})++a",[{parts, - 2}]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})++a",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})*+a",[trim]))), + 2}]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})++a",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})*+a",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})*+a",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})*+a",[]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})*+a",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a{2,3})*+a",[]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})*+a",[trim]))), <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})*+a",[{parts, - 2}]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})*+a",[]))), - <<"">> = iolist_to_binary(join(re:split("abXde","ab\\Cde",[trim]))), + 2}]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(a{2,3})*+a",[]))), + <<"">> = iolist_to_binary(join(re:split("abXde","ab\\Cde",[trim]))), <<":">> = iolist_to_binary(join(re:split("abXde","ab\\Cde",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abXde","ab\\Cde",[]))), - <<"abZde">> = iolist_to_binary(join(re:split("abZdeX","(?<=ab\\Cde)X",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abXde","ab\\Cde",[]))), + <<"abZde">> = iolist_to_binary(join(re:split("abZdeX","(?<=ab\\Cde)X",[trim]))), <<"abZde:">> = iolist_to_binary(join(re:split("abZdeX","(?<=ab\\Cde)X",[{parts, - 2}]))), - <<"abZde:">> = iolist_to_binary(join(re:split("abZdeX","(?<=ab\\Cde)X",[]))), - <<"">> = iolist_to_binary(join(re:split("aCb","a[\\CD]b",[trim]))), + 2}]))), + <<"abZde:">> = iolist_to_binary(join(re:split("abZdeX","(?<=ab\\Cde)X",[]))), + <<"">> = iolist_to_binary(join(re:split("aCb","a[\\CD]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("aCb","a[\\CD]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aCb","a[\\CD]b",[]))), - <<"">> = iolist_to_binary(join(re:split("aDb","a[\\CD]b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aCb","a[\\CD]b",[]))), + <<"">> = iolist_to_binary(join(re:split("aDb","a[\\CD]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("aDb","a[\\CD]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aDb","a[\\CD]b",[]))), - <<"">> = iolist_to_binary(join(re:split("aJb","a[\\C-X]b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aDb","a[\\CD]b",[]))), + <<"">> = iolist_to_binary(join(re:split("aJb","a[\\C-X]b",[trim]))), <<":">> = iolist_to_binary(join(re:split("aJb","a[\\C-X]b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aJb","a[\\C-X]b",[]))), - <<"X X">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aJb","a[\\C-X]b",[]))), + <<"X X">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[trim]))), <<"X X">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[{parts, - 2}]))), - <<"X X">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[]))), - <<"">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[trim]))), + 2}]))), + <<"X X">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[]))), + <<"">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[trim]))), <<":">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H\\h\\V\\v",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("X X","\\H\\h\\V\\v",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H\\h\\V\\v",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H\\h\\V\\v",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H\\h\\V\\v",[]))), - <<"Â X">> = iolist_to_binary(join(re:split("Â X","\\H\\h\\V\\v",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H\\h\\V\\v",[]))), + <<"Â X">> = iolist_to_binary(join(re:split("Â X","\\H\\h\\V\\v",[trim]))), <<"Â X">> = iolist_to_binary(join(re:split("Â X","\\H\\h\\V\\v",[{parts, - 2}]))), - <<"Â X">> = iolist_to_binary(join(re:split("Â X","\\H\\h\\V\\v",[]))), + 2}]))), + <<"Â X">> = iolist_to_binary(join(re:split("Â X","\\H\\h\\V\\v",[]))), <<"">> = iolist_to_binary(join(re:split(" Â X -
","\\H*\\h+\\V?\\v{3,4}",[trim]))), +
","\\H*\\h+\\V?\\v{3,4}",[trim]))), <<":">> = iolist_to_binary(join(re:split(" Â X -
","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), +
","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split(" Â X -
","\\H*\\h+\\V?\\v{3,4}",[]))), +
","\\H*\\h+\\V?\\v{3,4}",[]))), <<"">> = iolist_to_binary(join(re:split(" Â -
","\\H*\\h+\\V?\\v{3,4}",[trim]))), +
","\\H*\\h+\\V?\\v{3,4}",[trim]))), <<":">> = iolist_to_binary(join(re:split(" Â -
","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), +
","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split(" Â -
","\\H*\\h+\\V?\\v{3,4}",[]))), +
","\\H*\\h+\\V?\\v{3,4}",[]))), <<"">> = iolist_to_binary(join(re:split(" Â -","\\H*\\h+\\V?\\v{3,4}",[trim]))), +","\\H*\\h+\\V?\\v{3,4}",[trim]))), <<":">> = iolist_to_binary(join(re:split(" Â -","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), +","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), <<":">> = iolist_to_binary(join(re:split(" Â -","\\H*\\h+\\V?\\v{3,4}",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H*\\h+\\V?\\v{3,4}",[trim]))), +","\\H*\\h+\\V?\\v{3,4}",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H*\\h+\\V?\\v{3,4}",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H*\\h+\\V?\\v{3,4}",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H*\\h+\\V?\\v{3,4}",[]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\H*\\h+\\V?\\v{3,4}",[]))), <<" Â ">> = iolist_to_binary(join(re:split(" Â -","\\H*\\h+\\V?\\v{3,4}",[trim]))), +","\\H*\\h+\\V?\\v{3,4}",[trim]))), <<" Â ">> = iolist_to_binary(join(re:split(" Â -","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), +","\\H*\\h+\\V?\\v{3,4}",[{parts,2}]))), <<" Â ">> = iolist_to_binary(join(re:split(" Â -","\\H*\\h+\\V?\\v{3,4}",[]))), - <<"XY :E">> = iolist_to_binary(join(re:split("XY ABCDE","\\H{3,4}",[trim]))), +","\\H*\\h+\\V?\\v{3,4}",[]))), + <<"XY :E">> = iolist_to_binary(join(re:split("XY ABCDE","\\H{3,4}",[trim]))), <<"XY :E">> = iolist_to_binary(join(re:split("XY ABCDE","\\H{3,4}",[{parts, - 2}]))), - <<"XY :E">> = iolist_to_binary(join(re:split("XY ABCDE","\\H{3,4}",[]))), - <<"XY : ST">> = iolist_to_binary(join(re:split("XY PQR ST","\\H{3,4}",[trim]))), + 2}]))), + <<"XY :E">> = iolist_to_binary(join(re:split("XY ABCDE","\\H{3,4}",[]))), + <<"XY : ST">> = iolist_to_binary(join(re:split("XY PQR ST","\\H{3,4}",[trim]))), <<"XY : ST">> = iolist_to_binary(join(re:split("XY PQR ST","\\H{3,4}",[{parts, - 2}]))), - <<"XY : ST">> = iolist_to_binary(join(re:split("XY PQR ST","\\H{3,4}",[]))), - <<"XY A:QRS">> = iolist_to_binary(join(re:split("XY AB PQRS",".\\h{3,4}.",[trim]))), + 2}]))), + <<"XY : ST">> = iolist_to_binary(join(re:split("XY PQR ST","\\H{3,4}",[]))), + <<"XY A:QRS">> = iolist_to_binary(join(re:split("XY AB PQRS",".\\h{3,4}.",[trim]))), <<"XY A:QRS">> = iolist_to_binary(join(re:split("XY AB PQRS",".\\h{3,4}.",[{parts, - 2}]))), - <<"XY A:QRS">> = iolist_to_binary(join(re:split("XY AB PQRS",".\\h{3,4}.",[]))), - <<">">> = iolist_to_binary(join(re:split(">XNNNYZ","\\h*X\\h?\\H+Y\\H?Z",[trim]))), + 2}]))), + <<"XY A:QRS">> = iolist_to_binary(join(re:split("XY AB PQRS",".\\h{3,4}.",[]))), + <<">">> = iolist_to_binary(join(re:split(">XNNNYZ","\\h*X\\h?\\H+Y\\H?Z",[trim]))), <<">:">> = iolist_to_binary(join(re:split(">XNNNYZ","\\h*X\\h?\\H+Y\\H?Z",[{parts, - 2}]))), - <<">:">> = iolist_to_binary(join(re:split(">XNNNYZ","\\h*X\\h?\\H+Y\\H?Z",[]))), - <<">">> = iolist_to_binary(join(re:split("> X NYQZ","\\h*X\\h?\\H+Y\\H?Z",[trim]))), + 2}]))), + <<">:">> = iolist_to_binary(join(re:split(">XNNNYZ","\\h*X\\h?\\H+Y\\H?Z",[]))), + <<">">> = iolist_to_binary(join(re:split("> X NYQZ","\\h*X\\h?\\H+Y\\H?Z",[trim]))), <<">:">> = iolist_to_binary(join(re:split("> X NYQZ","\\h*X\\h?\\H+Y\\H?Z",[{parts, - 2}]))), - <<">:">> = iolist_to_binary(join(re:split("> X NYQZ","\\h*X\\h?\\H+Y\\H?Z",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\h*X\\h?\\H+Y\\H?Z",[trim]))), + 2}]))), + <<">:">> = iolist_to_binary(join(re:split("> X NYQZ","\\h*X\\h?\\H+Y\\H?Z",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\h*X\\h?\\H+Y\\H?Z",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\h*X\\h?\\H+Y\\H?Z",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\h*X\\h?\\H+Y\\H?Z",[]))), - <<">XYZ">> = iolist_to_binary(join(re:split(">XYZ","\\h*X\\h?\\H+Y\\H?Z",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\h*X\\h?\\H+Y\\H?Z",[]))), + <<">XYZ">> = iolist_to_binary(join(re:split(">XYZ","\\h*X\\h?\\H+Y\\H?Z",[trim]))), <<">XYZ">> = iolist_to_binary(join(re:split(">XYZ","\\h*X\\h?\\H+Y\\H?Z",[{parts, - 2}]))), - <<">XYZ">> = iolist_to_binary(join(re:split(">XYZ","\\h*X\\h?\\H+Y\\H?Z",[]))), - <<"> X NY Z">> = iolist_to_binary(join(re:split("> X NY Z","\\h*X\\h?\\H+Y\\H?Z",[trim]))), + 2}]))), + <<">XYZ">> = iolist_to_binary(join(re:split(">XYZ","\\h*X\\h?\\H+Y\\H?Z",[]))), + <<"> X NY Z">> = iolist_to_binary(join(re:split("> X NY Z","\\h*X\\h?\\H+Y\\H?Z",[trim]))), <<"> X NY Z">> = iolist_to_binary(join(re:split("> X NY Z","\\h*X\\h?\\H+Y\\H?Z",[{parts, - 2}]))), - <<"> X NY Z">> = iolist_to_binary(join(re:split("> X NY Z","\\h*X\\h?\\H+Y\\H?Z",[]))), + 2}]))), + <<"> X NY Z">> = iolist_to_binary(join(re:split("> X NY Z","\\h*X\\h?\\H+Y\\H?Z",[]))), <<">">> = iolist_to_binary(join(re:split(">XY Z -ANN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[trim]))), +ANN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[trim]))), <<">:">> = iolist_to_binary(join(re:split(">XY Z ANN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[{parts, - 2}]))), + 2}]))), <<">:">> = iolist_to_binary(join(re:split(">XY Z -ANN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[]))), +ANN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[]))), <<">">> = iolist_to_binary(join(re:split(">
X Y ZZZ -AAANNN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[trim]))), +AAANNN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[trim]))), <<">:">> = iolist_to_binary(join(re:split(">
X Y ZZZ AAANNN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[{parts, - 2}]))), + 2}]))), <<">:">> = iolist_to_binary(join(re:split(">
X Y ZZZ -AAANNN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[]))), - <<"foo:foo">> = iolist_to_binary(join(re:split("foobar","(foo)\\Kbar",[trim]))), +AAANNN","\\v*X\\v?Y\\v+Z\\V*\\x0a\\V+\\x0b\\V{2,3}\\x0c",[]))), + <<"foo:foo">> = iolist_to_binary(join(re:split("foobar","(foo)\\Kbar",[trim]))), <<"foo:foo:">> = iolist_to_binary(join(re:split("foobar","(foo)\\Kbar",[{parts, - 2}]))), - <<"foo:foo:">> = iolist_to_binary(join(re:split("foobar","(foo)\\Kbar",[]))), - <<"foo:foo:bar">> = iolist_to_binary(join(re:split("foobar","(foo)(\\Kbar|baz)",[trim]))), + 2}]))), + <<"foo:foo:">> = iolist_to_binary(join(re:split("foobar","(foo)\\Kbar",[]))), + <<"foo:foo:bar">> = iolist_to_binary(join(re:split("foobar","(foo)(\\Kbar|baz)",[trim]))), <<"foo:foo:bar:">> = iolist_to_binary(join(re:split("foobar","(foo)(\\Kbar|baz)",[{parts, - 2}]))), - <<"foo:foo:bar:">> = iolist_to_binary(join(re:split("foobar","(foo)(\\Kbar|baz)",[]))), - <<":foo:baz">> = iolist_to_binary(join(re:split("foobaz","(foo)(\\Kbar|baz)",[trim]))), + 2}]))), + <<"foo:foo:bar:">> = iolist_to_binary(join(re:split("foobar","(foo)(\\Kbar|baz)",[]))), + <<":foo:baz">> = iolist_to_binary(join(re:split("foobaz","(foo)(\\Kbar|baz)",[trim]))), <<":foo:baz:">> = iolist_to_binary(join(re:split("foobaz","(foo)(\\Kbar|baz)",[{parts, - 2}]))), - <<":foo:baz:">> = iolist_to_binary(join(re:split("foobaz","(foo)(\\Kbar|baz)",[]))), - <<"foo:foobar">> = iolist_to_binary(join(re:split("foobarbaz","(foo\\Kbar)baz",[trim]))), + 2}]))), + <<":foo:baz:">> = iolist_to_binary(join(re:split("foobaz","(foo)(\\Kbar|baz)",[]))), + <<"foo:foobar">> = iolist_to_binary(join(re:split("foobarbaz","(foo\\Kbar)baz",[trim]))), <<"foo:foobar:">> = iolist_to_binary(join(re:split("foobarbaz","(foo\\Kbar)baz",[{parts, - 2}]))), - <<"foo:foobar:">> = iolist_to_binary(join(re:split("foobarbaz","(foo\\Kbar)baz",[]))), - <<":tom">> = iolist_to_binary(join(re:split("tom-tom","(?<A>tom|bon)-\\g{A}",[trim]))), + 2}]))), + <<"foo:foobar:">> = iolist_to_binary(join(re:split("foobarbaz","(foo\\Kbar)baz",[]))), + <<":tom">> = iolist_to_binary(join(re:split("tom-tom","(?<A>tom|bon)-\\g{A}",[trim]))), <<":tom:">> = iolist_to_binary(join(re:split("tom-tom","(?<A>tom|bon)-\\g{A}",[{parts, - 2}]))), - <<":tom:">> = iolist_to_binary(join(re:split("tom-tom","(?<A>tom|bon)-\\g{A}",[]))), - <<":bon">> = iolist_to_binary(join(re:split("bon-bon","(?<A>tom|bon)-\\g{A}",[trim]))), + 2}]))), + <<":tom:">> = iolist_to_binary(join(re:split("tom-tom","(?<A>tom|bon)-\\g{A}",[]))), + <<":bon">> = iolist_to_binary(join(re:split("bon-bon","(?<A>tom|bon)-\\g{A}",[trim]))), <<":bon:">> = iolist_to_binary(join(re:split("bon-bon","(?<A>tom|bon)-\\g{A}",[{parts, - 2}]))), - <<":bon:">> = iolist_to_binary(join(re:split("bon-bon","(?<A>tom|bon)-\\g{A}",[]))), - <<"bacxxx">> = iolist_to_binary(join(re:split("bacxxx","(^(a|b\\g{-1}))",[trim]))), + 2}]))), + <<":bon:">> = iolist_to_binary(join(re:split("bon-bon","(?<A>tom|bon)-\\g{A}",[]))), + <<"bacxxx">> = iolist_to_binary(join(re:split("bacxxx","(^(a|b\\g{-1}))",[trim]))), <<"bacxxx">> = iolist_to_binary(join(re:split("bacxxx","(^(a|b\\g{-1}))",[{parts, - 2}]))), - <<"bacxxx">> = iolist_to_binary(join(re:split("bacxxx","(^(a|b\\g{-1}))",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))\\1",[trim]))), + 2}]))), + <<"bacxxx">> = iolist_to_binary(join(re:split("bacxxx","(^(a|b\\g{-1}))",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))\\1",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))\\1",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))\\1",[]))), - <<":xyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))\\1",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))\\1",[]))), + <<":xyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))\\1",[trim]))), <<":xyz:">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))\\1",[{parts, - 2}]))), - <<":xyz:">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))\\1",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))\\1",[trim]))), + 2}]))), + <<":xyz:">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))\\1",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))\\1",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))\\1",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))\\1",[]))), - <<"abcxyz">> = iolist_to_binary(join(re:split("abcxyz","(?|(abc)|(xyz))\\1",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))\\1",[]))), + <<"abcxyz">> = iolist_to_binary(join(re:split("abcxyz","(?|(abc)|(xyz))\\1",[trim]))), <<"abcxyz">> = iolist_to_binary(join(re:split("abcxyz","(?|(abc)|(xyz))\\1",[{parts, - 2}]))), - <<"abcxyz">> = iolist_to_binary(join(re:split("abcxyz","(?|(abc)|(xyz))\\1",[]))), - <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))\\1",[trim]))), + 2}]))), + <<"abcxyz">> = iolist_to_binary(join(re:split("abcxyz","(?|(abc)|(xyz))\\1",[]))), + <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))\\1",[trim]))), <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))\\1",[{parts, - 2}]))), - <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))\\1",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))(?1)",[trim]))), + 2}]))), + <<"xyzabc">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))\\1",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))(?1)",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))(?1)",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))(?1)",[]))), - <<":xyz">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))(?1)",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","(?|(abc)|(xyz))(?1)",[]))), + <<":xyz">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))(?1)",[trim]))), <<":xyz:">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))(?1)",[{parts, - 2}]))), - <<":xyz:">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))(?1)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))(?1)",[trim]))), + 2}]))), + <<":xyz:">> = iolist_to_binary(join(re:split("xyzabc","(?|(abc)|(xyz))(?1)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))(?1)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))(?1)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))(?1)",[]))), - <<"xyzxyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))(?1)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?|(abc)|(xyz))(?1)",[]))), + <<"xyzxyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))(?1)",[trim]))), <<"xyzxyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))(?1)",[{parts, - 2}]))), - <<"xyzxyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))(?1)",[]))), + 2}]))), + <<"xyzxyz">> = iolist_to_binary(join(re:split("xyzxyz","(?|(abc)|(xyz))(?1)",[]))), ok. run41() -> - <<":a:b:c:d:Y">> = iolist_to_binary(join(re:split("XYabcdY","^X(?5)(a)(?|(b)|(q))(c)(d)(Y)",[trim]))), + <<":a:b:c:d:Y">> = iolist_to_binary(join(re:split("XYabcdY","^X(?5)(a)(?|(b)|(q))(c)(d)(Y)",[trim]))), <<":a:b:c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?5)(a)(?|(b)|(q))(c)(d)(Y)",[{parts, - 2}]))), - <<":a:b:c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?5)(a)(?|(b)|(q))(c)(d)(Y)",[]))), - <<":a:b:::c:d:Y">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(r)(s))|(q))(c)(d)(Y)",[trim]))), + 2}]))), + <<":a:b:c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?5)(a)(?|(b)|(q))(c)(d)(Y)",[]))), + <<":a:b:::c:d:Y">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(r)(s))|(q))(c)(d)(Y)",[trim]))), <<":a:b:::c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(r)(s))|(q))(c)(d)(Y)",[{parts, - 2}]))), - <<":a:b:::c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(r)(s))|(q))(c)(d)(Y)",[]))), - <<":a:b:::c:d:Y">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(?|(r)|(t))(s))|(q))(c)(d)(Y)",[trim]))), + 2}]))), + <<":a:b:::c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(r)(s))|(q))(c)(d)(Y)",[]))), + <<":a:b:::c:d:Y">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(?|(r)|(t))(s))|(q))(c)(d)(Y)",[trim]))), <<":a:b:::c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(?|(r)|(t))(s))|(q))(c)(d)(Y)",[{parts, - 2}]))), - <<":a:b:::c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(?|(r)|(t))(s))|(q))(c)(d)(Y)",[]))), - <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), + 2}]))), + <<":a:b:::c:d:Y:">> = iolist_to_binary(join(re:split("XYabcdY","^X(?7)(a)(?|(b|(?|(r)|(t))(s))|(q))(c)(d)(Y)",[]))), + <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\k<abc>{2}",[{parts, - 2}]))), - <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\k<abc>{2}",[]))), - <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), + 2}]))), + <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\k<abc>{2}",[]))), + <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\k<abc>{2}",[{parts, - 2}]))), - <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\k<abc>{2}",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\k<abc>{2}",[trim]))), + 2}]))), + <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\k<abc>{2}",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\k<abc>{2}",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\k<abc>{2}",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\k<abc>{2}",[]))), - <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\k<abc>{2}",[]))), + <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\k<abc>{2}",[{parts, - 2}]))), - <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\k<abc>{2}",[]))), - <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), + 2}]))), + <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\k<abc>{2}",[]))), + <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\k<abc>{2}",[trim]))), <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\k<abc>{2}",[{parts, - 2}]))), - <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\k<abc>{2}",[]))), - <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), + 2}]))), + <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\k<abc>{2}",[]))), + <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\g{abc}{2}",[{parts, - 2}]))), - <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\g{abc}{2}",[]))), - <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), + 2}]))), + <<":a:xyz">> = iolist_to_binary(join(re:split("a:aaxyz","(?'abc'\\w+):\\g{abc}{2}",[]))), + <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\g{abc}{2}",[{parts, - 2}]))), - <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\g{abc}{2}",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\g{abc}{2}",[trim]))), + 2}]))), + <<":ab:xyz">> = iolist_to_binary(join(re:split("ab:ababxyz","(?'abc'\\w+):\\g{abc}{2}",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\g{abc}{2}",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\g{abc}{2}",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\g{abc}{2}",[]))), - <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?'abc'\\w+):\\g{abc}{2}",[]))), + <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\g{abc}{2}",[{parts, - 2}]))), - <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\g{abc}{2}",[]))), - <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), + 2}]))), + <<"a:axyz">> = iolist_to_binary(join(re:split("a:axyz","(?'abc'\\w+):\\g{abc}{2}",[]))), + <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\g{abc}{2}",[trim]))), <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\g{abc}{2}",[{parts, - 2}]))), - <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\g{abc}{2}",[]))), + 2}]))), + <<"ab:abxyz">> = iolist_to_binary(join(re:split("ab:abxyz","(?'abc'\\w+):\\g{abc}{2}",[]))), <<":a">> = iolist_to_binary(join(re:split("abd","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended, - trim]))), + trim]))), <<":a:">> = iolist_to_binary(join(re:split("abd","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended, {parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("abd","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("abd","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended]))), <<"">> = iolist_to_binary(join(re:split("ce","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended, - trim]))), + trim]))), <<"::">> = iolist_to_binary(join(re:split("ce","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended, {parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ce","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended]))), - <<":aX">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g-1Z",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ce","^(?<ab>a)? (?(<ab>)b|c) (?('ab')d|e)",[extended]))), + <<":aX">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g-1Z",[trim]))), <<":aX:">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g-1Z",[{parts, - 2}]))), - <<":aX:">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g-1Z",[]))), - <<":aX">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g{-1}Z",[trim]))), + 2}]))), + <<":aX:">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g-1Z",[]))), + <<":aX">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g{-1}Z",[trim]))), <<":aX:">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g{-1}Z",[{parts, - 2}]))), - <<":aX:">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g{-1}Z",[]))), + 2}]))), + <<":aX:">> = iolist_to_binary(join(re:split("aXaXZ","^(a.)\\g{-1}Z",[]))), <<":::cd">> = iolist_to_binary(join(re:split("abcd","^(?(DEFINE) (?<A> a) (?<B> b) ) (?&A) (?&B) ",[extended, - trim]))), + trim]))), <<":::cd">> = iolist_to_binary(join(re:split("abcd","^(?(DEFINE) (?<A> a) (?<B> b) ) (?&A) (?&B) ",[extended, {parts, - 2}]))), - <<":::cd">> = iolist_to_binary(join(re:split("abcd","^(?(DEFINE) (?<A> a) (?<B> b) ) (?&A) (?&B) ",[extended]))), + 2}]))), + <<":::cd">> = iolist_to_binary(join(re:split("abcd","^(?(DEFINE) (?<A> a) (?<B> b) ) (?&A) (?&B) ",[extended]))), <<":metcalfe:33">> = iolist_to_binary(join(re:split("metcalfe 33","(?<NAME>(?&NAME_PAT))\\s+(?<ADDR>(?&ADDRESS_PAT)) (?(DEFINE) (?<NAME_PAT>[a-z]+) (?<ADDRESS_PAT>\\d+) - )",[extended,trim]))), + )",[extended,trim]))), <<":metcalfe:33:::">> = iolist_to_binary(join(re:split("metcalfe 33","(?<NAME>(?&NAME_PAT))\\s+(?<ADDR>(?&ADDRESS_PAT)) (?(DEFINE) (?<NAME_PAT>[a-z]+) (?<ADDRESS_PAT>\\d+) - )",[extended,{parts,2}]))), + )",[extended,{parts,2}]))), <<":metcalfe:33:::">> = iolist_to_binary(join(re:split("metcalfe 33","(?<NAME>(?&NAME_PAT))\\s+(?<ADDR>(?&ADDRESS_PAT)) (?(DEFINE) (?<NAME_PAT>[a-z]+) (?<ADDRESS_PAT>\\d+) - )",[extended]))), - <<"::.4">> = iolist_to_binary(join(re:split("1.2.3.4","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), + )",[extended]))), + <<"::.4">> = iolist_to_binary(join(re:split("1.2.3.4","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), <<"::.4:">> = iolist_to_binary(join(re:split("1.2.3.4","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[{parts, - 2}]))), - <<"::.4:">> = iolist_to_binary(join(re:split("1.2.3.4","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), - <<"::.206">> = iolist_to_binary(join(re:split("131.111.10.206","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), + 2}]))), + <<"::.4:">> = iolist_to_binary(join(re:split("1.2.3.4","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), + <<"::.206">> = iolist_to_binary(join(re:split("131.111.10.206","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), <<"::.206:">> = iolist_to_binary(join(re:split("131.111.10.206","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[{parts, - 2}]))), - <<"::.206:">> = iolist_to_binary(join(re:split("131.111.10.206","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), - <<"::.0">> = iolist_to_binary(join(re:split("10.0.0.0","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), + 2}]))), + <<"::.206:">> = iolist_to_binary(join(re:split("131.111.10.206","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), + <<"::.0">> = iolist_to_binary(join(re:split("10.0.0.0","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), <<"::.0:">> = iolist_to_binary(join(re:split("10.0.0.0","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[{parts, - 2}]))), - <<"::.0:">> = iolist_to_binary(join(re:split("10.0.0.0","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), + 2}]))), + <<"::.0:">> = iolist_to_binary(join(re:split("10.0.0.0","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), - <<"10.6">> = iolist_to_binary(join(re:split("10.6","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), + <<"10.6">> = iolist_to_binary(join(re:split("10.6","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), <<"10.6">> = iolist_to_binary(join(re:split("10.6","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[{parts, - 2}]))), - <<"10.6">> = iolist_to_binary(join(re:split("10.6","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), - <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), + 2}]))), + <<"10.6">> = iolist_to_binary(join(re:split("10.6","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), + <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[trim]))), <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[{parts, - 2}]))), - <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), - <<":.4">> = iolist_to_binary(join(re:split("1.2.3.4","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), + 2}]))), + <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))\\b(?&byte)(\\.(?&byte)){3}",[]))), + <<":.4">> = iolist_to_binary(join(re:split("1.2.3.4","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), <<":.4::">> = iolist_to_binary(join(re:split("1.2.3.4","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[{parts, - 2}]))), - <<":.4::">> = iolist_to_binary(join(re:split("1.2.3.4","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), - <<":.206">> = iolist_to_binary(join(re:split("131.111.10.206","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), + 2}]))), + <<":.4::">> = iolist_to_binary(join(re:split("1.2.3.4","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), + <<":.206">> = iolist_to_binary(join(re:split("131.111.10.206","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), <<":.206::">> = iolist_to_binary(join(re:split("131.111.10.206","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[{parts, - 2}]))), - <<":.206::">> = iolist_to_binary(join(re:split("131.111.10.206","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), - <<":.0">> = iolist_to_binary(join(re:split("10.0.0.0","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), + 2}]))), + <<":.206::">> = iolist_to_binary(join(re:split("131.111.10.206","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), + <<":.0">> = iolist_to_binary(join(re:split("10.0.0.0","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), <<":.0::">> = iolist_to_binary(join(re:split("10.0.0.0","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[{parts, - 2}]))), - <<":.0::">> = iolist_to_binary(join(re:split("10.0.0.0","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), + 2}]))), + <<":.0::">> = iolist_to_binary(join(re:split("10.0.0.0","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), - <<"10.6">> = iolist_to_binary(join(re:split("10.6","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), + <<"10.6">> = iolist_to_binary(join(re:split("10.6","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), <<"10.6">> = iolist_to_binary(join(re:split("10.6","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[{parts, - 2}]))), - <<"10.6">> = iolist_to_binary(join(re:split("10.6","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), - <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), + 2}]))), + <<"10.6">> = iolist_to_binary(join(re:split("10.6","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), + <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[trim]))), <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[{parts, - 2}]))), - <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), - <<":party">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^(\\w++|\\s++)*$",[trim]))), + 2}]))), + <<"455.3.4.5">> = iolist_to_binary(join(re:split("455.3.4.5","\\b(?&byte)(\\.(?&byte)){3}(?(DEFINE)(?<byte>2[0-4]\\d|25[0-5]|1\\d\\d|[1-9]?\\d))",[]))), + <<":party">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^(\\w++|\\s++)*$",[trim]))), <<":party:">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^(\\w++|\\s++)*$",[{parts, - 2}]))), - <<":party:">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^(\\w++|\\s++)*$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\w++|\\s++)*$",[trim]))), + 2}]))), + <<":party:">> = iolist_to_binary(join(re:split("now is the time for all good men to come to the aid of the party","^(\\w++|\\s++)*$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\w++|\\s++)*$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\w++|\\s++)*$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\w++|\\s++)*$",[]))), - <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^(\\w++|\\s++)*$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\w++|\\s++)*$",[]))), + <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^(\\w++|\\s++)*$",[trim]))), <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^(\\w++|\\s++)*$",[{parts, - 2}]))), - <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^(\\w++|\\s++)*$",[]))), - <<":12345:a">> = iolist_to_binary(join(re:split("12345a","(\\d++)(\\w)",[trim]))), + 2}]))), + <<"this is not a line with only words and spaces!">> = iolist_to_binary(join(re:split("this is not a line with only words and spaces!","^(\\w++|\\s++)*$",[]))), + <<":12345:a">> = iolist_to_binary(join(re:split("12345a","(\\d++)(\\w)",[trim]))), <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","(\\d++)(\\w)",[{parts, - 2}]))), - <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","(\\d++)(\\w)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\d++)(\\w)",[trim]))), + 2}]))), + <<":12345:a:">> = iolist_to_binary(join(re:split("12345a","(\\d++)(\\w)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\d++)(\\w)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\d++)(\\w)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\d++)(\\w)",[]))), - <<"12345+">> = iolist_to_binary(join(re:split("12345+","(\\d++)(\\w)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","(\\d++)(\\w)",[]))), + <<"12345+">> = iolist_to_binary(join(re:split("12345+","(\\d++)(\\w)",[trim]))), <<"12345+">> = iolist_to_binary(join(re:split("12345+","(\\d++)(\\w)",[{parts, - 2}]))), - <<"12345+">> = iolist_to_binary(join(re:split("12345+","(\\d++)(\\w)",[]))), - <<"">> = iolist_to_binary(join(re:split("aaab","a++b",[trim]))), + 2}]))), + <<"12345+">> = iolist_to_binary(join(re:split("12345+","(\\d++)(\\w)",[]))), + <<"">> = iolist_to_binary(join(re:split("aaab","a++b",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaab","a++b",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaab","a++b",[]))), - <<":aaab">> = iolist_to_binary(join(re:split("aaab","(a++b)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaab","a++b",[]))), + <<":aaab">> = iolist_to_binary(join(re:split("aaab","(a++b)",[trim]))), <<":aaab:">> = iolist_to_binary(join(re:split("aaab","(a++b)",[{parts, - 2}]))), - <<":aaab:">> = iolist_to_binary(join(re:split("aaab","(a++b)",[]))), - <<":aaa">> = iolist_to_binary(join(re:split("aaab","(a++)b",[trim]))), + 2}]))), + <<":aaab:">> = iolist_to_binary(join(re:split("aaab","(a++b)",[]))), + <<":aaa">> = iolist_to_binary(join(re:split("aaab","(a++)b",[trim]))), <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(a++)b",[{parts, - 2}]))), - <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(a++)b",[]))), - <<"((:x">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","([^()]++|\\([^()]*\\))+",[trim]))), + 2}]))), + <<":aaa:">> = iolist_to_binary(join(re:split("aaab","(a++)b",[]))), + <<"((:x">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","([^()]++|\\([^()]*\\))+",[trim]))), <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","([^()]++|\\([^()]*\\))+",[{parts, - 2}]))), - <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","([^()]++|\\([^()]*\\))+",[]))), - <<":abc">> = iolist_to_binary(join(re:split("(abc)","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<"((:x:">> = iolist_to_binary(join(re:split("((abc(ade)ufh()()x","([^()]++|\\([^()]*\\))+",[]))), + <<":abc">> = iolist_to_binary(join(re:split("(abc)","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("(abc)","\\(([^()]++|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("(abc)","\\(([^()]++|\\([^()]+\\))+\\)",[]))), - <<":xyz">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("(abc)","\\(([^()]++|\\([^()]+\\))+\\)",[]))), + <<":xyz">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), <<":xyz:">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(([^()]++|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<":xyz:">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(([^()]++|\\([^()]+\\))+\\)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<":xyz:">> = iolist_to_binary(join(re:split("(abc(def)xyz)","\\(([^()]++|\\([^()]+\\))+\\)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(([^()]++|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(([^()]++|\\([^()]+\\))+\\)",[]))), - <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","\\(([^()]++|\\([^()]+\\))+\\)",[]))), + <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(([^()]++|\\([^()]+\\))+\\)",[trim]))), <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(([^()]++|\\([^()]+\\))+\\)",[{parts, - 2}]))), - <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(([^()]++|\\([^()]+\\))+\\)",[]))), - <<":c">> = iolist_to_binary(join(re:split("abc","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<"((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa">> = iolist_to_binary(join(re:split("((()aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa","\\(([^()]++|\\([^()]+\\))+\\)",[]))), + <<":c">> = iolist_to_binary(join(re:split("abc","^([^()]|\\((?1)*\\))*$",[trim]))), <<":c:">> = iolist_to_binary(join(re:split("abc","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<":c:">> = iolist_to_binary(join(re:split("abc","^([^()]|\\((?1)*\\))*$",[]))), - <<":c">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<":c:">> = iolist_to_binary(join(re:split("abc","^([^()]|\\((?1)*\\))*$",[]))), + <<":c">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[trim]))), <<":c:">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<":c:">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[]))), - <<":d">> = iolist_to_binary(join(re:split("a(b(c))d","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<":c:">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[]))), + <<":d">> = iolist_to_binary(join(re:split("a(b(c))d","^([^()]|\\((?1)*\\))*$",[trim]))), <<":d:">> = iolist_to_binary(join(re:split("a(b(c))d","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<":d:">> = iolist_to_binary(join(re:split("a(b(c))d","^([^()]|\\((?1)*\\))*$",[]))), - <<"*** Failers)">> = iolist_to_binary(join(re:split("*** Failers)","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<":d:">> = iolist_to_binary(join(re:split("a(b(c))d","^([^()]|\\((?1)*\\))*$",[]))), + <<"*** Failers)">> = iolist_to_binary(join(re:split("*** Failers)","^([^()]|\\((?1)*\\))*$",[trim]))), <<"*** Failers)">> = iolist_to_binary(join(re:split("*** Failers)","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<"*** Failers)">> = iolist_to_binary(join(re:split("*** Failers)","^([^()]|\\((?1)*\\))*$",[]))), - <<"a(b(c)d">> = iolist_to_binary(join(re:split("a(b(c)d","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<"*** Failers)">> = iolist_to_binary(join(re:split("*** Failers)","^([^()]|\\((?1)*\\))*$",[]))), + <<"a(b(c)d">> = iolist_to_binary(join(re:split("a(b(c)d","^([^()]|\\((?1)*\\))*$",[trim]))), <<"a(b(c)d">> = iolist_to_binary(join(re:split("a(b(c)d","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<"a(b(c)d">> = iolist_to_binary(join(re:split("a(b(c)d","^([^()]|\\((?1)*\\))*$",[]))), + 2}]))), + <<"a(b(c)d">> = iolist_to_binary(join(re:split("a(b(c)d","^([^()]|\\((?1)*\\))*$",[]))), ok. run42() -> - <<":3">> = iolist_to_binary(join(re:split(">abc>123<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[trim]))), + <<":3">> = iolist_to_binary(join(re:split(">abc>123<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[trim]))), <<":3:">> = iolist_to_binary(join(re:split(">abc>123<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[{parts, - 2}]))), - <<":3:">> = iolist_to_binary(join(re:split(">abc>123<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[]))), - <<":3">> = iolist_to_binary(join(re:split(">abc>1(2)3<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[trim]))), + 2}]))), + <<":3:">> = iolist_to_binary(join(re:split(">abc>123<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[]))), + <<":3">> = iolist_to_binary(join(re:split(">abc>1(2)3<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[trim]))), <<":3:">> = iolist_to_binary(join(re:split(">abc>1(2)3<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[{parts, - 2}]))), - <<":3:">> = iolist_to_binary(join(re:split(">abc>1(2)3<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[]))), - <<":(1(2)3)">> = iolist_to_binary(join(re:split(">abc>(1(2)3)<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[trim]))), + 2}]))), + <<":3:">> = iolist_to_binary(join(re:split(">abc>1(2)3<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[]))), + <<":(1(2)3)">> = iolist_to_binary(join(re:split(">abc>(1(2)3)<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[trim]))), <<":(1(2)3):">> = iolist_to_binary(join(re:split(">abc>(1(2)3)<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[{parts, - 2}]))), - <<":(1(2)3):">> = iolist_to_binary(join(re:split(">abc>(1(2)3)<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[]))), + 2}]))), + <<":(1(2)3):">> = iolist_to_binary(join(re:split(">abc>(1(2)3)<xyz<","^>abc>([^()]|\\((?1)*\\))*<xyz<$",[]))), <<":1221:1">> = iolist_to_binary(join(re:split("1221","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, - trim]))), + trim]))), <<":1221:1:::">> = iolist_to_binary(join(re:split("1221","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, {parts, - 2}]))), - <<":1221:1:::">> = iolist_to_binary(join(re:split("1221","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), + 2}]))), + <<":1221:1:::">> = iolist_to_binary(join(re:split("1221","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), <<":::Satanoscillatemymetallicsonatas:S">> = iolist_to_binary(join(re:split("Satanoscillatemymetallicsonatas","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, - trim]))), + trim]))), <<":::Satanoscillatemymetallicsonatas:S:">> = iolist_to_binary(join(re:split("Satanoscillatemymetallicsonatas","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, {parts, - 2}]))), - <<":::Satanoscillatemymetallicsonatas:S:">> = iolist_to_binary(join(re:split("Satanoscillatemymetallicsonatas","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), + 2}]))), + <<":::Satanoscillatemymetallicsonatas:S:">> = iolist_to_binary(join(re:split("Satanoscillatemymetallicsonatas","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), <<":::AmanaplanacanalPanama:A">> = iolist_to_binary(join(re:split("AmanaplanacanalPanama","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, - trim]))), + trim]))), <<":::AmanaplanacanalPanama:A:">> = iolist_to_binary(join(re:split("AmanaplanacanalPanama","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, {parts, - 2}]))), - <<":::AmanaplanacanalPanama:A:">> = iolist_to_binary(join(re:split("AmanaplanacanalPanama","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), + 2}]))), + <<":::AmanaplanacanalPanama:A:">> = iolist_to_binary(join(re:split("AmanaplanacanalPanama","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), <<":::AblewasIereIsawElba:A">> = iolist_to_binary(join(re:split("AblewasIereIsawElba","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, - trim]))), + trim]))), <<":::AblewasIereIsawElba:A:">> = iolist_to_binary(join(re:split("AblewasIereIsawElba","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, {parts, - 2}]))), - <<":::AblewasIereIsawElba:A:">> = iolist_to_binary(join(re:split("AblewasIereIsawElba","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), + 2}]))), + <<":::AblewasIereIsawElba:A:">> = iolist_to_binary(join(re:split("AblewasIereIsawElba","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), <<"Thequickbrownfox">> = iolist_to_binary(join(re:split("Thequickbrownfox","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, - trim]))), + trim]))), <<"Thequickbrownfox">> = iolist_to_binary(join(re:split("Thequickbrownfox","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless, {parts, - 2}]))), - <<"Thequickbrownfox">> = iolist_to_binary(join(re:split("Thequickbrownfox","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), - <<":12">> = iolist_to_binary(join(re:split("12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), + 2}]))), + <<"Thequickbrownfox">> = iolist_to_binary(join(re:split("Thequickbrownfox","^(?:((.)(?1)\\2|)|((.)(?3)\\4|.))$",[caseless]))), + <<":12">> = iolist_to_binary(join(re:split("12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), <<":12::">> = iolist_to_binary(join(re:split("12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[{parts, - 2}]))), - <<":12::">> = iolist_to_binary(join(re:split("12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), - <<":(((2+2)*-3)-7):-">> = iolist_to_binary(join(re:split("(((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), + 2}]))), + <<":12::">> = iolist_to_binary(join(re:split("12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), + <<":(((2+2)*-3)-7):-">> = iolist_to_binary(join(re:split("(((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), <<":(((2+2)*-3)-7):-:">> = iolist_to_binary(join(re:split("(((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[{parts, - 2}]))), - <<":(((2+2)*-3)-7):-:">> = iolist_to_binary(join(re:split("(((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), - <<":-12">> = iolist_to_binary(join(re:split("-12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), + 2}]))), + <<":(((2+2)*-3)-7):-:">> = iolist_to_binary(join(re:split("(((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), + <<":-12">> = iolist_to_binary(join(re:split("-12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), <<":-12::">> = iolist_to_binary(join(re:split("-12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[{parts, - 2}]))), - <<":-12::">> = iolist_to_binary(join(re:split("-12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), + 2}]))), + <<":-12::">> = iolist_to_binary(join(re:split("-12","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), - <<"((2+2)*-3)-7)">> = iolist_to_binary(join(re:split("((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), + <<"((2+2)*-3)-7)">> = iolist_to_binary(join(re:split("((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[trim]))), <<"((2+2)*-3)-7)">> = iolist_to_binary(join(re:split("((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[{parts, - 2}]))), - <<"((2+2)*-3)-7)">> = iolist_to_binary(join(re:split("((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), - <<":xyz:y">> = iolist_to_binary(join(re:split("xyz","^(x(y|(?1){2})z)",[trim]))), + 2}]))), + <<"((2+2)*-3)-7)">> = iolist_to_binary(join(re:split("((2+2)*-3)-7)","^(\\d+|\\((?1)([+*-])(?1)\\)|-(?1))$",[]))), + <<":xyz:y">> = iolist_to_binary(join(re:split("xyz","^(x(y|(?1){2})z)",[trim]))), <<":xyz:y:">> = iolist_to_binary(join(re:split("xyz","^(x(y|(?1){2})z)",[{parts, - 2}]))), - <<":xyz:y:">> = iolist_to_binary(join(re:split("xyz","^(x(y|(?1){2})z)",[]))), - <<":xxyzxyzz:xyzxyz">> = iolist_to_binary(join(re:split("xxyzxyzz","^(x(y|(?1){2})z)",[trim]))), + 2}]))), + <<":xyz:y:">> = iolist_to_binary(join(re:split("xyz","^(x(y|(?1){2})z)",[]))), + <<":xxyzxyzz:xyzxyz">> = iolist_to_binary(join(re:split("xxyzxyzz","^(x(y|(?1){2})z)",[trim]))), <<":xxyzxyzz:xyzxyz:">> = iolist_to_binary(join(re:split("xxyzxyzz","^(x(y|(?1){2})z)",[{parts, - 2}]))), - <<":xxyzxyzz:xyzxyz:">> = iolist_to_binary(join(re:split("xxyzxyzz","^(x(y|(?1){2})z)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(x(y|(?1){2})z)",[trim]))), + 2}]))), + <<":xxyzxyzz:xyzxyz:">> = iolist_to_binary(join(re:split("xxyzxyzz","^(x(y|(?1){2})z)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(x(y|(?1){2})z)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(x(y|(?1){2})z)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(x(y|(?1){2})z)",[]))), - <<"xxyzz">> = iolist_to_binary(join(re:split("xxyzz","^(x(y|(?1){2})z)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(x(y|(?1){2})z)",[]))), + <<"xxyzz">> = iolist_to_binary(join(re:split("xxyzz","^(x(y|(?1){2})z)",[trim]))), <<"xxyzz">> = iolist_to_binary(join(re:split("xxyzz","^(x(y|(?1){2})z)",[{parts, - 2}]))), - <<"xxyzz">> = iolist_to_binary(join(re:split("xxyzz","^(x(y|(?1){2})z)",[]))), - <<"xxyzxyzxyzz">> = iolist_to_binary(join(re:split("xxyzxyzxyzz","^(x(y|(?1){2})z)",[trim]))), + 2}]))), + <<"xxyzz">> = iolist_to_binary(join(re:split("xxyzz","^(x(y|(?1){2})z)",[]))), + <<"xxyzxyzxyzz">> = iolist_to_binary(join(re:split("xxyzxyzxyzz","^(x(y|(?1){2})z)",[trim]))), <<"xxyzxyzxyzz">> = iolist_to_binary(join(re:split("xxyzxyzxyzz","^(x(y|(?1){2})z)",[{parts, - 2}]))), - <<"xxyzxyzxyzz">> = iolist_to_binary(join(re:split("xxyzxyzxyzz","^(x(y|(?1){2})z)",[]))), + 2}]))), + <<"xxyzxyzxyzz">> = iolist_to_binary(join(re:split("xxyzxyzxyzz","^(x(y|(?1){2})z)",[]))), <<":<>:<>">> = iolist_to_binary(join(re:split("<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<":<>:<>:">> = iolist_to_binary(join(re:split("<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<":<>:<>:">> = iolist_to_binary(join(re:split("<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<":<>:<>:">> = iolist_to_binary(join(re:split("<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<":<abcd>:<abcd>">> = iolist_to_binary(join(re:split("<abcd>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<":<abcd>:<abcd>:">> = iolist_to_binary(join(re:split("<abcd>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<":<abcd>:<abcd>:">> = iolist_to_binary(join(re:split("<abcd>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<":<abcd>:<abcd>:">> = iolist_to_binary(join(re:split("<abcd>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<":<abc <123> hij>:<abc <123> hij>">> = iolist_to_binary(join(re:split("<abc <123> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<":<abc <123> hij>:<abc <123> hij>:">> = iolist_to_binary(join(re:split("<abc <123> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<":<abc <123> hij>:<abc <123> hij>:">> = iolist_to_binary(join(re:split("<abc <123> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<":<abc <123> hij>:<abc <123> hij>:">> = iolist_to_binary(join(re:split("<abc <123> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<"<abc :<def>:<def>: hij>">> = iolist_to_binary(join(re:split("<abc <def> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<"<abc :<def>:<def>: hij>">> = iolist_to_binary(join(re:split("<abc <def> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<"<abc :<def>:<def>: hij>">> = iolist_to_binary(join(re:split("<abc <def> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<"<abc :<def>:<def>: hij>">> = iolist_to_binary(join(re:split("<abc <def> hij>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<":<abc<>def>:<abc<>def>">> = iolist_to_binary(join(re:split("<abc<>def>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<":<abc<>def>:<abc<>def>:">> = iolist_to_binary(join(re:split("<abc<>def>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<":<abc<>def>:<abc<>def>:">> = iolist_to_binary(join(re:split("<abc<>def>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<":<abc<>def>:<abc<>def>:">> = iolist_to_binary(join(re:split("<abc<>def>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<"<abc:<>:<>">> = iolist_to_binary(join(re:split("<abc<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<"<abc:<>:<>:">> = iolist_to_binary(join(re:split("<abc<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<"<abc:<>:<>:">> = iolist_to_binary(join(re:split("<abc<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<"<abc:<>:<>:">> = iolist_to_binary(join(re:split("<abc<>","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), <<"<abc">> = iolist_to_binary(join(re:split("<abc","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, - trim]))), + trim]))), <<"<abc">> = iolist_to_binary(join(re:split("<abc","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended, {parts, - 2}]))), - <<"<abc">> = iolist_to_binary(join(re:split("<abc","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^a+(*FAIL)",[trim]))), + 2}]))), + <<"<abc">> = iolist_to_binary(join(re:split("<abc","((< (?: (?(R) \\d++ | [^<>]*+) | (?2)) * >))",[extended]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^a+(*FAIL)",[trim]))), <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^a+(*FAIL)",[{parts, - 2}]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^a+(*FAIL)",[]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?c+(*FAIL)",[trim]))), + 2}]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^a+(*FAIL)",[]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?c+(*FAIL)",[trim]))), <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?c+(*FAIL)",[{parts, - 2}]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?c+(*FAIL)",[]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*PRUNE)c+(*FAIL)",[trim]))), + 2}]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?c+(*FAIL)",[]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*PRUNE)c+(*FAIL)",[trim]))), <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*PRUNE)c+(*FAIL)",[{parts, - 2}]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*PRUNE)c+(*FAIL)",[]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*COMMIT)c+(*FAIL)",[trim]))), + 2}]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*PRUNE)c+(*FAIL)",[]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*COMMIT)c+(*FAIL)",[trim]))), <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*COMMIT)c+(*FAIL)",[{parts, - 2}]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*COMMIT)c+(*FAIL)",[]))), - <<"aaabcccaaabccc">> = iolist_to_binary(join(re:split("aaabcccaaabccc","a+b?(*SKIP)c+(*FAIL)",[trim]))), + 2}]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*COMMIT)c+(*FAIL)",[]))), + <<"aaabcccaaabccc">> = iolist_to_binary(join(re:split("aaabcccaaabccc","a+b?(*SKIP)c+(*FAIL)",[trim]))), <<"aaabcccaaabccc">> = iolist_to_binary(join(re:split("aaabcccaaabccc","a+b?(*SKIP)c+(*FAIL)",[{parts, - 2}]))), - <<"aaabcccaaabccc">> = iolist_to_binary(join(re:split("aaabcccaaabccc","a+b?(*SKIP)c+(*FAIL)",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<"aaabcccaaabccc">> = iolist_to_binary(join(re:split("aaabcccaaabccc","a+b?(*SKIP)c+(*FAIL)",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<"">> = iolist_to_binary(join(re:split("bbbxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<"">> = iolist_to_binary(join(re:split("bbbxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":">> = iolist_to_binary(join(re:split("bbbxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("bbbxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("bbbxxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<"">> = iolist_to_binary(join(re:split("cccxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<"">> = iolist_to_binary(join(re:split("cccxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":">> = iolist_to_binary(join(re:split("cccxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("cccxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":++++">> = iolist_to_binary(join(re:split("ccc++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("cccxxxx","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":++++">> = iolist_to_binary(join(re:split("ccc++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":++++">> = iolist_to_binary(join(re:split("ccc++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":++++">> = iolist_to_binary(join(re:split("ccc++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":++++">> = iolist_to_binary(join(re:split("ccc++++","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":aaaxxxxxx">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(?:aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":aaaxxxxxx">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":aaaxxxxxx:">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":aaaxxxxxx:">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":aaa:++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":aaaxxxxxx:">> = iolist_to_binary(join(re:split("aaaxxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":aaa:++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":aaa:++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":aaa:++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":bbbxxxxx">> = iolist_to_binary(join(re:split("bbbxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":aaa:++++++">> = iolist_to_binary(join(re:split("aaa++++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":bbbxxxxx">> = iolist_to_binary(join(re:split("bbbxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":bbbxxxxx:">> = iolist_to_binary(join(re:split("bbbxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":bbbxxxxx:">> = iolist_to_binary(join(re:split("bbbxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":bbb:+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":bbbxxxxx:">> = iolist_to_binary(join(re:split("bbbxxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":bbb:+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":bbb:+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":bbb:+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":cccxxxx">> = iolist_to_binary(join(re:split("cccxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":bbb:+++++">> = iolist_to_binary(join(re:split("bbb+++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":cccxxxx">> = iolist_to_binary(join(re:split("cccxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":cccxxxx:">> = iolist_to_binary(join(re:split("cccxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":cccxxxx:">> = iolist_to_binary(join(re:split("cccxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":ccc:++++">> = iolist_to_binary(join(re:split("ccc++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":cccxxxx:">> = iolist_to_binary(join(re:split("cccxxxx","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":ccc:++++">> = iolist_to_binary(join(re:split("ccc++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":ccc:++++">> = iolist_to_binary(join(re:split("ccc++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":ccc:++++">> = iolist_to_binary(join(re:split("ccc++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<":ddd:ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), + 2}]))), + <<":ccc:++++">> = iolist_to_binary(join(re:split("ccc++++","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<":ddd:ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[trim]))), <<":ddd:ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[{parts, - 2}]))), - <<":ddd:ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*THEN)c+(*FAIL)",[trim]))), + 2}]))), + <<":ddd:ddddd">> = iolist_to_binary(join(re:split("dddddddd","^(aaa(*THEN)\\w{6}|bbb(*THEN)\\w{5}|ccc(*THEN)\\w{4}|\\w{3})",[]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*THEN)c+(*FAIL)",[trim]))), <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*THEN)c+(*FAIL)",[{parts, - 2}]))), - <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*THEN)c+(*FAIL)",[]))), + 2}]))), + <<"aaabccc">> = iolist_to_binary(join(re:split("aaabccc","a+b?(*THEN)c+(*FAIL)",[]))), <<":AB:B">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<":AB:B::">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<":AB:B::">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<":AB:B::">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), <<":AB:B::X">> = iolist_to_binary(join(re:split("ABX","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<":AB:B::X">> = iolist_to_binary(join(re:split("ABX","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<":AB:B::X">> = iolist_to_binary(join(re:split("ABX","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<":AB:B::X">> = iolist_to_binary(join(re:split("ABX","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), <<":AAD:A:E">> = iolist_to_binary(join(re:split("AADE","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<":AAD:A:E:">> = iolist_to_binary(join(re:split("AADE","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<":AAD:A:E:">> = iolist_to_binary(join(re:split("AADE","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<":AAD:A:E:">> = iolist_to_binary(join(re:split("AADE","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), <<":ACD:C:E">> = iolist_to_binary(join(re:split("ACDE","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<":ACD:C:E:">> = iolist_to_binary(join(re:split("ACDE","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<":ACD:C:E:">> = iolist_to_binary(join(re:split("ACDE","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<":ACD:C:E:">> = iolist_to_binary(join(re:split("ACDE","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), <<"AD">> = iolist_to_binary(join(re:split("AD","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<"AD">> = iolist_to_binary(join(re:split("AD","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<"AD">> = iolist_to_binary(join(re:split("AD","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<"AD">> = iolist_to_binary(join(re:split("AD","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), <<":1221:1">> = iolist_to_binary(join(re:split("1221","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, - trim]))), + trim]))), <<":1221:1:::">> = iolist_to_binary(join(re:split("1221","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, {parts, - 2}]))), - <<":1221:1:::">> = iolist_to_binary(join(re:split("1221","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), + 2}]))), + <<":1221:1:::">> = iolist_to_binary(join(re:split("1221","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), <<":::Satan, oscillate my metallic sonatas:S">> = iolist_to_binary(join(re:split("Satan, oscillate my metallic sonatas!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, - trim]))), + trim]))), <<":::Satan, oscillate my metallic sonatas:S:">> = iolist_to_binary(join(re:split("Satan, oscillate my metallic sonatas!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, {parts, - 2}]))), - <<":::Satan, oscillate my metallic sonatas:S:">> = iolist_to_binary(join(re:split("Satan, oscillate my metallic sonatas!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), + 2}]))), + <<":::Satan, oscillate my metallic sonatas:S:">> = iolist_to_binary(join(re:split("Satan, oscillate my metallic sonatas!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), <<":::A man, a plan, a canal: Panama:A">> = iolist_to_binary(join(re:split("A man, a plan, a canal: Panama!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, - trim]))), + trim]))), <<":::A man, a plan, a canal: Panama:A:">> = iolist_to_binary(join(re:split("A man, a plan, a canal: Panama!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, {parts, - 2}]))), - <<":::A man, a plan, a canal: Panama:A:">> = iolist_to_binary(join(re:split("A man, a plan, a canal: Panama!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), + 2}]))), + <<":::A man, a plan, a canal: Panama:A:">> = iolist_to_binary(join(re:split("A man, a plan, a canal: Panama!","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), <<":::Able was I ere I saw Elba:A">> = iolist_to_binary(join(re:split("Able was I ere I saw Elba.","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, - trim]))), + trim]))), <<":::Able was I ere I saw Elba:A:">> = iolist_to_binary(join(re:split("Able was I ere I saw Elba.","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, {parts, - 2}]))), - <<":::Able was I ere I saw Elba:A:">> = iolist_to_binary(join(re:split("Able was I ere I saw Elba.","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), + 2}]))), + <<":::Able was I ere I saw Elba:A:">> = iolist_to_binary(join(re:split("Able was I ere I saw Elba.","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, - trim]))), + trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, {parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, - trim]))), + trim]))), <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless, {parts, - 2}]))), - <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), - <<":a">> = iolist_to_binary(join(re:split("a","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<"The quick brown fox">> = iolist_to_binary(join(re:split("The quick brown fox","^\\W*+(?:((.)\\W*+(?1)\\W*+\\2|)|((.)\\W*+(?3)\\W*+\\4|\\W*+.\\W*+))\\W*+$",[caseless]))), + <<":a">> = iolist_to_binary(join(re:split("a","^((.)(?1)\\2|.)$",[trim]))), <<":a::">> = iolist_to_binary(join(re:split("a","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<":a::">> = iolist_to_binary(join(re:split("a","^((.)(?1)\\2|.)$",[]))), - <<":aba:a">> = iolist_to_binary(join(re:split("aba","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<":a::">> = iolist_to_binary(join(re:split("a","^((.)(?1)\\2|.)$",[]))), + <<":aba:a">> = iolist_to_binary(join(re:split("aba","^((.)(?1)\\2|.)$",[trim]))), <<":aba:a:">> = iolist_to_binary(join(re:split("aba","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<":aba:a:">> = iolist_to_binary(join(re:split("aba","^((.)(?1)\\2|.)$",[]))), - <<":aabaa:a">> = iolist_to_binary(join(re:split("aabaa","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<":aba:a:">> = iolist_to_binary(join(re:split("aba","^((.)(?1)\\2|.)$",[]))), + <<":aabaa:a">> = iolist_to_binary(join(re:split("aabaa","^((.)(?1)\\2|.)$",[trim]))), <<":aabaa:a:">> = iolist_to_binary(join(re:split("aabaa","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<":aabaa:a:">> = iolist_to_binary(join(re:split("aabaa","^((.)(?1)\\2|.)$",[]))), - <<":abcdcba:a">> = iolist_to_binary(join(re:split("abcdcba","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<":aabaa:a:">> = iolist_to_binary(join(re:split("aabaa","^((.)(?1)\\2|.)$",[]))), + <<":abcdcba:a">> = iolist_to_binary(join(re:split("abcdcba","^((.)(?1)\\2|.)$",[trim]))), <<":abcdcba:a:">> = iolist_to_binary(join(re:split("abcdcba","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<":abcdcba:a:">> = iolist_to_binary(join(re:split("abcdcba","^((.)(?1)\\2|.)$",[]))), - <<":pqaabaaqp:p">> = iolist_to_binary(join(re:split("pqaabaaqp","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<":abcdcba:a:">> = iolist_to_binary(join(re:split("abcdcba","^((.)(?1)\\2|.)$",[]))), + <<":pqaabaaqp:p">> = iolist_to_binary(join(re:split("pqaabaaqp","^((.)(?1)\\2|.)$",[trim]))), <<":pqaabaaqp:p:">> = iolist_to_binary(join(re:split("pqaabaaqp","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<":pqaabaaqp:p:">> = iolist_to_binary(join(re:split("pqaabaaqp","^((.)(?1)\\2|.)$",[]))), - <<":ablewasiereisawelba:a">> = iolist_to_binary(join(re:split("ablewasiereisawelba","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<":pqaabaaqp:p:">> = iolist_to_binary(join(re:split("pqaabaaqp","^((.)(?1)\\2|.)$",[]))), + <<":ablewasiereisawelba:a">> = iolist_to_binary(join(re:split("ablewasiereisawelba","^((.)(?1)\\2|.)$",[trim]))), <<":ablewasiereisawelba:a:">> = iolist_to_binary(join(re:split("ablewasiereisawelba","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<":ablewasiereisawelba:a:">> = iolist_to_binary(join(re:split("ablewasiereisawelba","^((.)(?1)\\2|.)$",[]))), - <<"rhubarb">> = iolist_to_binary(join(re:split("rhubarb","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<":ablewasiereisawelba:a:">> = iolist_to_binary(join(re:split("ablewasiereisawelba","^((.)(?1)\\2|.)$",[]))), + <<"rhubarb">> = iolist_to_binary(join(re:split("rhubarb","^((.)(?1)\\2|.)$",[trim]))), <<"rhubarb">> = iolist_to_binary(join(re:split("rhubarb","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<"rhubarb">> = iolist_to_binary(join(re:split("rhubarb","^((.)(?1)\\2|.)$",[]))), - <<"the quick brown fox">> = iolist_to_binary(join(re:split("the quick brown fox","^((.)(?1)\\2|.)$",[trim]))), + 2}]))), + <<"rhubarb">> = iolist_to_binary(join(re:split("rhubarb","^((.)(?1)\\2|.)$",[]))), + <<"the quick brown fox">> = iolist_to_binary(join(re:split("the quick brown fox","^((.)(?1)\\2|.)$",[trim]))), <<"the quick brown fox">> = iolist_to_binary(join(re:split("the quick brown fox","^((.)(?1)\\2|.)$",[{parts, - 2}]))), - <<"the quick brown fox">> = iolist_to_binary(join(re:split("the quick brown fox","^((.)(?1)\\2|.)$",[]))), - <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(a)(?<=b(?1))",[trim]))), + 2}]))), + <<"the quick brown fox">> = iolist_to_binary(join(re:split("the quick brown fox","^((.)(?1)\\2|.)$",[]))), + <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(a)(?<=b(?1))",[trim]))), <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(a)(?<=b(?1))",[{parts, - 2}]))), - <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(a)(?<=b(?1))",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)(?<=b(?1))",[trim]))), + 2}]))), + <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(a)(?<=b(?1))",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)(?<=b(?1))",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)(?<=b(?1))",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)(?<=b(?1))",[]))), - <<"caz">> = iolist_to_binary(join(re:split("caz","(a)(?<=b(?1))",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)(?<=b(?1))",[]))), + <<"caz">> = iolist_to_binary(join(re:split("caz","(a)(?<=b(?1))",[trim]))), <<"caz">> = iolist_to_binary(join(re:split("caz","(a)(?<=b(?1))",[{parts, - 2}]))), - <<"caz">> = iolist_to_binary(join(re:split("caz","(a)(?<=b(?1))",[]))), - <<"zba:a:z">> = iolist_to_binary(join(re:split("zbaaz","(?<=b(?1))(a)",[trim]))), + 2}]))), + <<"caz">> = iolist_to_binary(join(re:split("caz","(a)(?<=b(?1))",[]))), + <<"zba:a:z">> = iolist_to_binary(join(re:split("zbaaz","(?<=b(?1))(a)",[trim]))), <<"zba:a:z">> = iolist_to_binary(join(re:split("zbaaz","(?<=b(?1))(a)",[{parts, - 2}]))), - <<"zba:a:z">> = iolist_to_binary(join(re:split("zbaaz","(?<=b(?1))(a)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=b(?1))(a)",[trim]))), + 2}]))), + <<"zba:a:z">> = iolist_to_binary(join(re:split("zbaaz","(?<=b(?1))(a)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=b(?1))(a)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=b(?1))(a)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=b(?1))(a)",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","(?<=b(?1))(a)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=b(?1))(a)",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","(?<=b(?1))(a)",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","(?<=b(?1))(a)",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","(?<=b(?1))(a)",[]))), - <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(?<X>a)(?<=b(?&X))",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","(?<=b(?1))(a)",[]))), + <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(?<X>a)(?<=b(?&X))",[trim]))), <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(?<X>a)(?<=b(?&X))",[{parts, - 2}]))), - <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(?<X>a)(?<=b(?&X))",[]))), - <<":abc">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))\\1",[trim]))), + 2}]))), + <<"b:a:z">> = iolist_to_binary(join(re:split("baz","(?<X>a)(?<=b(?&X))",[]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))\\1",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))\\1",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))\\1",[]))), - <<":def">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))\\1",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))\\1",[]))), + <<":def">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))\\1",[trim]))), <<":def:">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))\\1",[{parts, - 2}]))), - <<":def:">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))\\1",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))\\1",[trim]))), + 2}]))), + <<":def:">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))\\1",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))\\1",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))\\1",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))\\1",[]))), - <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))\\1",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))\\1",[]))), + <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))\\1",[trim]))), <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))\\1",[{parts, - 2}]))), - <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))\\1",[]))), - <<"defabc">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))\\1",[trim]))), + 2}]))), + <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))\\1",[]))), + <<"defabc">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))\\1",[trim]))), <<"defabc">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))\\1",[{parts, - 2}]))), - <<"defabc">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))\\1",[]))), + 2}]))), + <<"defabc">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))\\1",[]))), ok. run43() -> - <<":abc">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))(?1)",[trim]))), + <<":abc">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))(?1)",[trim]))), <<":abc:">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))(?1)",[{parts, - 2}]))), - <<":abc:">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))(?1)",[]))), - <<":def">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))(?1)",[trim]))), + 2}]))), + <<":abc:">> = iolist_to_binary(join(re:split("abcabc","^(?|(abc)|(def))(?1)",[]))), + <<":def">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))(?1)",[trim]))), <<":def:">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))(?1)",[{parts, - 2}]))), - <<":def:">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))(?1)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))(?1)",[trim]))), + 2}]))), + <<":def:">> = iolist_to_binary(join(re:split("defabc","^(?|(abc)|(def))(?1)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))(?1)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))(?1)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))(?1)",[]))), - <<"defdef">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))(?1)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?|(abc)|(def))(?1)",[]))), + <<"defdef">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))(?1)",[trim]))), <<"defdef">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))(?1)",[{parts, - 2}]))), - <<"defdef">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))(?1)",[]))), - <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))(?1)",[trim]))), + 2}]))), + <<"defdef">> = iolist_to_binary(join(re:split("defdef","^(?|(abc)|(def))(?1)",[]))), + <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))(?1)",[trim]))), <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))(?1)",[{parts, - 2}]))), - <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))(?1)",[]))), - <<"A:C:D">> = iolist_to_binary(join(re:split("ABCD","(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"abcdef">> = iolist_to_binary(join(re:split("abcdef","^(?|(abc)|(def))(?1)",[]))), + <<"A:C:D">> = iolist_to_binary(join(re:split("ABCD","(?:(?1)|B)(A(*F)|C)",[trim]))), <<"A:C:D">> = iolist_to_binary(join(re:split("ABCD","(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"A:C:D">> = iolist_to_binary(join(re:split("ABCD","(?:(?1)|B)(A(*F)|C)",[]))), - <<":C:D">> = iolist_to_binary(join(re:split("CCD","(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"A:C:D">> = iolist_to_binary(join(re:split("ABCD","(?:(?1)|B)(A(*F)|C)",[]))), + <<":C:D">> = iolist_to_binary(join(re:split("CCD","(?:(?1)|B)(A(*F)|C)",[trim]))), <<":C:D">> = iolist_to_binary(join(re:split("CCD","(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<":C:D">> = iolist_to_binary(join(re:split("CCD","(?:(?1)|B)(A(*F)|C)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<":C:D">> = iolist_to_binary(join(re:split("CCD","(?:(?1)|B)(A(*F)|C)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*F)|C)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*F)|C)",[]))), - <<"CAD">> = iolist_to_binary(join(re:split("CAD","(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*F)|C)",[]))), + <<"CAD">> = iolist_to_binary(join(re:split("CAD","(?:(?1)|B)(A(*F)|C)",[trim]))), <<"CAD">> = iolist_to_binary(join(re:split("CAD","(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"CAD">> = iolist_to_binary(join(re:split("CAD","(?:(?1)|B)(A(*F)|C)",[]))), - <<":C:D">> = iolist_to_binary(join(re:split("CCD","^(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"CAD">> = iolist_to_binary(join(re:split("CAD","(?:(?1)|B)(A(*F)|C)",[]))), + <<":C:D">> = iolist_to_binary(join(re:split("CCD","^(?:(?1)|B)(A(*F)|C)",[trim]))), <<":C:D">> = iolist_to_binary(join(re:split("CCD","^(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<":C:D">> = iolist_to_binary(join(re:split("CCD","^(?:(?1)|B)(A(*F)|C)",[]))), - <<":C:D">> = iolist_to_binary(join(re:split("BCD","^(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<":C:D">> = iolist_to_binary(join(re:split("CCD","^(?:(?1)|B)(A(*F)|C)",[]))), + <<":C:D">> = iolist_to_binary(join(re:split("BCD","^(?:(?1)|B)(A(*F)|C)",[trim]))), <<":C:D">> = iolist_to_binary(join(re:split("BCD","^(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<":C:D">> = iolist_to_binary(join(re:split("BCD","^(?:(?1)|B)(A(*F)|C)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<":C:D">> = iolist_to_binary(join(re:split("BCD","^(?:(?1)|B)(A(*F)|C)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:(?1)|B)(A(*F)|C)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:(?1)|B)(A(*F)|C)",[]))), - <<"ABCD">> = iolist_to_binary(join(re:split("ABCD","^(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:(?1)|B)(A(*F)|C)",[]))), + <<"ABCD">> = iolist_to_binary(join(re:split("ABCD","^(?:(?1)|B)(A(*F)|C)",[trim]))), <<"ABCD">> = iolist_to_binary(join(re:split("ABCD","^(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"ABCD">> = iolist_to_binary(join(re:split("ABCD","^(?:(?1)|B)(A(*F)|C)",[]))), - <<"CAD">> = iolist_to_binary(join(re:split("CAD","^(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"ABCD">> = iolist_to_binary(join(re:split("ABCD","^(?:(?1)|B)(A(*F)|C)",[]))), + <<"CAD">> = iolist_to_binary(join(re:split("CAD","^(?:(?1)|B)(A(*F)|C)",[trim]))), <<"CAD">> = iolist_to_binary(join(re:split("CAD","^(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"CAD">> = iolist_to_binary(join(re:split("CAD","^(?:(?1)|B)(A(*F)|C)",[]))), - <<"BAD">> = iolist_to_binary(join(re:split("BAD","^(?:(?1)|B)(A(*F)|C)",[trim]))), + 2}]))), + <<"CAD">> = iolist_to_binary(join(re:split("CAD","^(?:(?1)|B)(A(*F)|C)",[]))), + <<"BAD">> = iolist_to_binary(join(re:split("BAD","^(?:(?1)|B)(A(*F)|C)",[trim]))), <<"BAD">> = iolist_to_binary(join(re:split("BAD","^(?:(?1)|B)(A(*F)|C)",[{parts, - 2}]))), - <<"BAD">> = iolist_to_binary(join(re:split("BAD","^(?:(?1)|B)(A(*F)|C)",[]))), - <<":A:D">> = iolist_to_binary(join(re:split("AAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<"BAD">> = iolist_to_binary(join(re:split("BAD","^(?:(?1)|B)(A(*F)|C)",[]))), + <<":A:D">> = iolist_to_binary(join(re:split("AAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<":A:D">> = iolist_to_binary(join(re:split("AAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<":A:D">> = iolist_to_binary(join(re:split("AAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<":C">> = iolist_to_binary(join(re:split("ACD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<":A:D">> = iolist_to_binary(join(re:split("AAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<":C">> = iolist_to_binary(join(re:split("ACD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<":C:">> = iolist_to_binary(join(re:split("ACD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<":C:">> = iolist_to_binary(join(re:split("ACD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<":A:D">> = iolist_to_binary(join(re:split("BAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<":C:">> = iolist_to_binary(join(re:split("ACD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<":A:D">> = iolist_to_binary(join(re:split("BAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<":A:D">> = iolist_to_binary(join(re:split("BAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<":A:D">> = iolist_to_binary(join(re:split("BAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<":C">> = iolist_to_binary(join(re:split("BCD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<":A:D">> = iolist_to_binary(join(re:split("BAD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<":C">> = iolist_to_binary(join(re:split("BCD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<":C:">> = iolist_to_binary(join(re:split("BCD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<":C:">> = iolist_to_binary(join(re:split("BCD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<":A:X">> = iolist_to_binary(join(re:split("BAX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<":C:">> = iolist_to_binary(join(re:split("BCD","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<":A:X">> = iolist_to_binary(join(re:split("BAX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<":A:X">> = iolist_to_binary(join(re:split("BAX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<":A:X">> = iolist_to_binary(join(re:split("BAX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<":A:X">> = iolist_to_binary(join(re:split("BAX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<"ACX">> = iolist_to_binary(join(re:split("ACX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<"ACX">> = iolist_to_binary(join(re:split("ACX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<"ACX">> = iolist_to_binary(join(re:split("ACX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<"ACX">> = iolist_to_binary(join(re:split("ACX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), + 2}]))), + <<"ACX">> = iolist_to_binary(join(re:split("ACX","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[trim]))), <<"ABC">> = iolist_to_binary(join(re:split("ABC","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[{parts, - 2}]))), - <<"ABC">> = iolist_to_binary(join(re:split("ABC","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), - <<"">> = iolist_to_binary(join(re:split("BAC","(?(DEFINE)(A))B(?1)C",[trim]))), + 2}]))), + <<"ABC">> = iolist_to_binary(join(re:split("ABC","(?:(?1)|B)(A(*ACCEPT)XX|C)D",[]))), + <<"">> = iolist_to_binary(join(re:split("BAC","(?(DEFINE)(A))B(?1)C",[trim]))), <<"::">> = iolist_to_binary(join(re:split("BAC","(?(DEFINE)(A))B(?1)C",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("BAC","(?(DEFINE)(A))B(?1)C",[]))), - <<"">> = iolist_to_binary(join(re:split("BAAC","(?(DEFINE)((A)\\2))B(?1)C",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("BAC","(?(DEFINE)(A))B(?1)C",[]))), + <<"">> = iolist_to_binary(join(re:split("BAAC","(?(DEFINE)((A)\\2))B(?1)C",[trim]))), <<":::">> = iolist_to_binary(join(re:split("BAAC","(?(DEFINE)((A)\\2))B(?1)C",[{parts, - 2}]))), - <<":::">> = iolist_to_binary(join(re:split("BAAC","(?(DEFINE)((A)\\2))B(?1)C",[]))), + 2}]))), + <<":::">> = iolist_to_binary(join(re:split("BAAC","(?(DEFINE)((A)\\2))B(?1)C",[]))), <<":(ab(cd)ef):ef">> = iolist_to_binary(join(re:split("(ab(cd)ef)","(?<pn> \\( ( [^()]++ | (?&pn) )* \\) )",[extended, - trim]))), + trim]))), <<":(ab(cd)ef):ef:">> = iolist_to_binary(join(re:split("(ab(cd)ef)","(?<pn> \\( ( [^()]++ | (?&pn) )* \\) )",[extended, {parts, - 2}]))), - <<":(ab(cd)ef):ef:">> = iolist_to_binary(join(re:split("(ab(cd)ef)","(?<pn> \\( ( [^()]++ | (?&pn) )* \\) )",[extended]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*SKIP)b|ac)",[trim]))), + 2}]))), + <<":(ab(cd)ef):ef:">> = iolist_to_binary(join(re:split("(ab(cd)ef)","(?<pn> \\( ( [^()]++ | (?&pn) )* \\) )",[extended]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*SKIP)b|ac)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*SKIP)b|ac)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*SKIP)b|ac)",[]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*SKIP)b|ac)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*SKIP)b|ac)",[]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*SKIP)b|ac)",[trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*SKIP)b|ac)",[{parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*SKIP)b|ac)",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(?=a(*PRUNE)b)",[trim]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*SKIP)b|ac)",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(?=a(*PRUNE)b)",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","^(?=a(*PRUNE)b)",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","^(?=a(*PRUNE)b)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*PRUNE)b)",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","^(?=a(*PRUNE)b)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*PRUNE)b)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*PRUNE)b)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*PRUNE)b)",[]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*PRUNE)b)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?=a(*PRUNE)b)",[]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*PRUNE)b)",[trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*PRUNE)b)",[{parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*PRUNE)b)",[]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*ACCEPT)b)",[trim]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*PRUNE)b)",[]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*ACCEPT)b)",[trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*ACCEPT)b)",[{parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*ACCEPT)b)",[]))), - <<"a">> = iolist_to_binary(join(re:split("ab","(?>a\\Kb)",[trim]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?=a(*ACCEPT)b)",[]))), + <<"a">> = iolist_to_binary(join(re:split("ab","(?>a\\Kb)",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("ab","(?>a\\Kb)",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("ab","(?>a\\Kb)",[]))), - <<"a:ab">> = iolist_to_binary(join(re:split("ab","((?>a\\Kb))",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("ab","(?>a\\Kb)",[]))), + <<"a:ab">> = iolist_to_binary(join(re:split("ab","((?>a\\Kb))",[trim]))), <<"a:ab:">> = iolist_to_binary(join(re:split("ab","((?>a\\Kb))",[{parts, - 2}]))), - <<"a:ab:">> = iolist_to_binary(join(re:split("ab","((?>a\\Kb))",[]))), - <<"a:ab">> = iolist_to_binary(join(re:split("ab","(a\\Kb)",[trim]))), + 2}]))), + <<"a:ab:">> = iolist_to_binary(join(re:split("ab","((?>a\\Kb))",[]))), + <<"a:ab">> = iolist_to_binary(join(re:split("ab","(a\\Kb)",[trim]))), <<"a:ab:">> = iolist_to_binary(join(re:split("ab","(a\\Kb)",[{parts, - 2}]))), - <<"a:ab:">> = iolist_to_binary(join(re:split("ab","(a\\Kb)",[]))), - <<"">> = iolist_to_binary(join(re:split("ac","^a\\Kcz|ac",[trim]))), + 2}]))), + <<"a:ab:">> = iolist_to_binary(join(re:split("ab","(a\\Kb)",[]))), + <<"">> = iolist_to_binary(join(re:split("ac","^a\\Kcz|ac",[trim]))), <<":">> = iolist_to_binary(join(re:split("ac","^a\\Kcz|ac",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ac","^a\\Kcz|ac",[]))), - <<"">> = iolist_to_binary(join(re:split("ab","(?>a\\Kbz|ab)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ac","^a\\Kcz|ac",[]))), + <<"">> = iolist_to_binary(join(re:split("ab","(?>a\\Kbz|ab)",[trim]))), <<":">> = iolist_to_binary(join(re:split("ab","(?>a\\Kbz|ab)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ab","(?>a\\Kbz|ab)",[]))), - <<"a">> = iolist_to_binary(join(re:split("ab","^(?&t)(?(DEFINE)(?<t>a\\Kb))$",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ab","(?>a\\Kbz|ab)",[]))), + <<"a">> = iolist_to_binary(join(re:split("ab","^(?&t)(?(DEFINE)(?<t>a\\Kb))$",[trim]))), <<"a::">> = iolist_to_binary(join(re:split("ab","^(?&t)(?(DEFINE)(?<t>a\\Kb))$",[{parts, - 2}]))), - <<"a::">> = iolist_to_binary(join(re:split("ab","^(?&t)(?(DEFINE)(?<t>a\\Kb))$",[]))), - <<":c">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<"a::">> = iolist_to_binary(join(re:split("ab","^(?&t)(?(DEFINE)(?<t>a\\Kb))$",[]))), + <<":c">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[trim]))), <<":c:">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<":c:">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[]))), - <<":e">> = iolist_to_binary(join(re:split("a(b(c)d)e","^([^()]|\\((?1)*\\))*$",[trim]))), + 2}]))), + <<":c:">> = iolist_to_binary(join(re:split("a(b)c","^([^()]|\\((?1)*\\))*$",[]))), + <<":e">> = iolist_to_binary(join(re:split("a(b(c)d)e","^([^()]|\\((?1)*\\))*$",[trim]))), <<":e:">> = iolist_to_binary(join(re:split("a(b(c)d)e","^([^()]|\\((?1)*\\))*$",[{parts, - 2}]))), - <<":e:">> = iolist_to_binary(join(re:split("a(b(c)d)e","^([^()]|\\((?1)*\\))*$",[]))), - <<":0">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[trim]))), + 2}]))), + <<":e:">> = iolist_to_binary(join(re:split("a(b(c)d)e","^([^()]|\\((?1)*\\))*$",[]))), + <<":0">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[trim]))), <<":0::">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[{parts, - 2}]))), - <<":0::">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[]))), - <<":00:0">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[trim]))), + 2}]))), + <<":0::">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[]))), + <<":00:0">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[trim]))), <<":00:0:">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[{parts, - 2}]))), - <<":00:0:">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[]))), - <<":0000:0">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[trim]))), + 2}]))), + <<":00:0:">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[]))), + <<":0000:0">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[trim]))), <<":0000:0:">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[{parts, - 2}]))), - <<":0000:0:">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[]))), - <<":0:0">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[trim]))), + 2}]))), + <<":0000:0:">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)(?P>L1)|(?P>L2))",[]))), + <<":0:0">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[trim]))), <<":0:0:">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[{parts, - 2}]))), - <<":0:0:">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[]))), - <<":0:0::0:0">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[trim]))), + 2}]))), + <<":0:0:">> = iolist_to_binary(join(re:split("0","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[]))), + <<":0:0::0:0">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[trim]))), <<":0:0:0">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[{parts, - 2}]))), - <<":0:0::0:0:">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[]))), - <<":0:0::0:0::0:0::0:0">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[trim]))), + 2}]))), + <<":0:0::0:0:">> = iolist_to_binary(join(re:split("00","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[]))), + <<":0:0::0:0::0:0::0:0">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[trim]))), <<":0:0:000">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[{parts, - 2}]))), - <<":0:0::0:0::0:0::0:0:">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[]))), + 2}]))), + <<":0:0::0:0::0:0::0:0:">> = iolist_to_binary(join(re:split("0000","(?P<L1>(?P<L2>0)|(?P>L2)(?P>L1))",[]))), ok. run44() -> - <<"ACABX">> = iolist_to_binary(join(re:split("ACABX","A(*COMMIT)(B|D)",[trim]))), + <<"ACABX">> = iolist_to_binary(join(re:split("ACABX","A(*COMMIT)(B|D)",[trim]))), <<"ACABX">> = iolist_to_binary(join(re:split("ACABX","A(*COMMIT)(B|D)",[{parts, - 2}]))), - <<"ACABX">> = iolist_to_binary(join(re:split("ACABX","A(*COMMIT)(B|D)",[]))), - <<":A:B:C:DEFG">> = iolist_to_binary(join(re:split("ABCDEFG","(*COMMIT)(A|P)(B|P)(C|P)",[trim]))), + 2}]))), + <<"ACABX">> = iolist_to_binary(join(re:split("ACABX","A(*COMMIT)(B|D)",[]))), + <<":A:B:C:DEFG">> = iolist_to_binary(join(re:split("ABCDEFG","(*COMMIT)(A|P)(B|P)(C|P)",[trim]))), <<":A:B:C:DEFG">> = iolist_to_binary(join(re:split("ABCDEFG","(*COMMIT)(A|P)(B|P)(C|P)",[{parts, - 2}]))), - <<":A:B:C:DEFG">> = iolist_to_binary(join(re:split("ABCDEFG","(*COMMIT)(A|P)(B|P)(C|P)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(*COMMIT)(A|P)(B|P)(C|P)",[trim]))), + 2}]))), + <<":A:B:C:DEFG">> = iolist_to_binary(join(re:split("ABCDEFG","(*COMMIT)(A|P)(B|P)(C|P)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(*COMMIT)(A|P)(B|P)(C|P)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(*COMMIT)(A|P)(B|P)(C|P)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(*COMMIT)(A|P)(B|P)(C|P)",[]))), - <<"DEFGABC">> = iolist_to_binary(join(re:split("DEFGABC","(*COMMIT)(A|P)(B|P)(C|P)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(*COMMIT)(A|P)(B|P)(C|P)",[]))), + <<"DEFGABC">> = iolist_to_binary(join(re:split("DEFGABC","(*COMMIT)(A|P)(B|P)(C|P)",[trim]))), <<"DEFGABC">> = iolist_to_binary(join(re:split("DEFGABC","(*COMMIT)(A|P)(B|P)(C|P)",[{parts, - 2}]))), - <<"DEFGABC">> = iolist_to_binary(join(re:split("DEFGABC","(*COMMIT)(A|P)(B|P)(C|P)",[]))), - <<":a">> = iolist_to_binary(join(re:split("abbb","(\\w+)(?>b(*COMMIT))\\w{2}",[trim]))), + 2}]))), + <<"DEFGABC">> = iolist_to_binary(join(re:split("DEFGABC","(*COMMIT)(A|P)(B|P)(C|P)",[]))), + <<":a">> = iolist_to_binary(join(re:split("abbb","(\\w+)(?>b(*COMMIT))\\w{2}",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("abbb","(\\w+)(?>b(*COMMIT))\\w{2}",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("abbb","(\\w+)(?>b(*COMMIT))\\w{2}",[]))), - <<"abbb">> = iolist_to_binary(join(re:split("abbb","(\\w+)b(*COMMIT)\\w{2}",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("abbb","(\\w+)(?>b(*COMMIT))\\w{2}",[]))), + <<"abbb">> = iolist_to_binary(join(re:split("abbb","(\\w+)b(*COMMIT)\\w{2}",[trim]))), <<"abbb">> = iolist_to_binary(join(re:split("abbb","(\\w+)b(*COMMIT)\\w{2}",[{parts, - 2}]))), - <<"abbb">> = iolist_to_binary(join(re:split("abbb","(\\w+)b(*COMMIT)\\w{2}",[]))), - <<"b::c">> = iolist_to_binary(join(re:split("bac","(?&t)(?#()(?(DEFINE)(?<t>a))",[trim]))), + 2}]))), + <<"abbb">> = iolist_to_binary(join(re:split("abbb","(\\w+)b(*COMMIT)\\w{2}",[]))), + <<"b::c">> = iolist_to_binary(join(re:split("bac","(?&t)(?#()(?(DEFINE)(?<t>a))",[trim]))), <<"b::c">> = iolist_to_binary(join(re:split("bac","(?&t)(?#()(?(DEFINE)(?<t>a))",[{parts, - 2}]))), - <<"b::c">> = iolist_to_binary(join(re:split("bac","(?&t)(?#()(?(DEFINE)(?<t>a))",[]))), - <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(?>yes|no)(*THEN)(*F))?",[trim]))), + 2}]))), + <<"b::c">> = iolist_to_binary(join(re:split("bac","(?&t)(?#()(?(DEFINE)(?<t>a))",[]))), + <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(?>yes|no)(*THEN)(*F))?",[trim]))), <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(?>yes|no)(*THEN)(*F))?",[{parts, - 2}]))), - <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(?>yes|no)(*THEN)(*F))?",[]))), - <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(yes|no)(*THEN)(*F))?",[trim]))), + 2}]))), + <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(?>yes|no)(*THEN)(*F))?",[]))), + <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(yes|no)(*THEN)(*F))?",[trim]))), <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(yes|no)(*THEN)(*F))?",[{parts, - 2}]))), - <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(yes|no)(*THEN)(*F))?",[]))), - <<"">> = iolist_to_binary(join(re:split("bc","b?(*SKIP)c",[trim]))), + 2}]))), + <<"yes">> = iolist_to_binary(join(re:split("yes","(?>(*COMMIT)(yes|no)(*THEN)(*F))?",[]))), + <<"">> = iolist_to_binary(join(re:split("bc","b?(*SKIP)c",[trim]))), <<":">> = iolist_to_binary(join(re:split("bc","b?(*SKIP)c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("bc","b?(*SKIP)c",[]))), - <<"a">> = iolist_to_binary(join(re:split("abc","b?(*SKIP)c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("bc","b?(*SKIP)c",[]))), + <<"a">> = iolist_to_binary(join(re:split("abc","b?(*SKIP)c",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("abc","b?(*SKIP)c",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("abc","b?(*SKIP)c",[]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("abc","b?(*SKIP)c",[]))), ok. run45() -> - <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)bc",[trim]))), + <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)bc",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)bc",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)bc",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)b",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)bc",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)b",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)b",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)b",[]))), - <<"x::x::x">> = iolist_to_binary(join(re:split("xxx","(?P<abn>(?P=abn)xxx|)+",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","(*SKIP)b",[]))), + <<"x::x::x">> = iolist_to_binary(join(re:split("xxx","(?P<abn>(?P=abn)xxx|)+",[trim]))), <<"x::xx">> = iolist_to_binary(join(re:split("xxx","(?P<abn>(?P=abn)xxx|)+",[{parts, - 2}]))), - <<"x::x::x::">> = iolist_to_binary(join(re:split("xxx","(?P<abn>(?P=abn)xxx|)+",[]))), - <<":a">> = iolist_to_binary(join(re:split("aa","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<"x::x::x::">> = iolist_to_binary(join(re:split("xxx","(?P<abn>(?P=abn)xxx|)+",[]))), + <<":a">> = iolist_to_binary(join(re:split("aa","(?i:([^b]))(?1)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aa","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aa","(?i:([^b]))(?1)",[]))), - <<":a">> = iolist_to_binary(join(re:split("aA","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aa","(?i:([^b]))(?1)",[]))), + <<":a">> = iolist_to_binary(join(re:split("aA","(?i:([^b]))(?1)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aA","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aA","(?i:([^b]))(?1)",[]))), - <<":*:: ::a::l::r">> = iolist_to_binary(join(re:split("** Failers","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aA","(?i:([^b]))(?1)",[]))), + <<":*:: ::a::l::r">> = iolist_to_binary(join(re:split("** Failers","(?i:([^b]))(?1)",[trim]))), <<":*: Failers">> = iolist_to_binary(join(re:split("** Failers","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<":*:: ::a::l::r:">> = iolist_to_binary(join(re:split("** Failers","(?i:([^b]))(?1)",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<":*:: ::a::l::r:">> = iolist_to_binary(join(re:split("** Failers","(?i:([^b]))(?1)",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","(?i:([^b]))(?1)",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","(?i:([^b]))(?1)",[]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","(?i:([^b]))(?1)",[]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:([^b]))(?1)",[trim]))), <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:([^b]))(?1)",[]))), - <<"Ba">> = iolist_to_binary(join(re:split("Ba","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<"aB">> = iolist_to_binary(join(re:split("aB","(?i:([^b]))(?1)",[]))), + <<"Ba">> = iolist_to_binary(join(re:split("Ba","(?i:([^b]))(?1)",[trim]))), <<"Ba">> = iolist_to_binary(join(re:split("Ba","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<"Ba">> = iolist_to_binary(join(re:split("Ba","(?i:([^b]))(?1)",[]))), - <<"ba">> = iolist_to_binary(join(re:split("ba","(?i:([^b]))(?1)",[trim]))), + 2}]))), + <<"Ba">> = iolist_to_binary(join(re:split("Ba","(?i:([^b]))(?1)",[]))), + <<"ba">> = iolist_to_binary(join(re:split("ba","(?i:([^b]))(?1)",[trim]))), <<"ba">> = iolist_to_binary(join(re:split("ba","(?i:([^b]))(?1)",[{parts, - 2}]))), - <<"ba">> = iolist_to_binary(join(re:split("ba","(?i:([^b]))(?1)",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[trim]))), + 2}]))), + <<"ba">> = iolist_to_binary(join(re:split("ba","(?i:([^b]))(?1)",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[trim]))), <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[{parts, - 2}]))), - <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[trim]))), + 2}]))), + <<"aaaaaa">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*+(?(DEFINE)(?<t>a))\\w$",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaaaaaX","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[]))), - <<":a:X">> = iolist_to_binary(join(re:split("aaaaX","^(a)*+(\\w)",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aaaaaa","^(?&t)*(?(DEFINE)(?<t>a))\\w$",[]))), + <<":a:X">> = iolist_to_binary(join(re:split("aaaaX","^(a)*+(\\w)",[trim]))), <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a)*+(\\w)",[{parts, - 2}]))), - <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a)*+(\\w)",[]))), - <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)*+(\\w)",[trim]))), + 2}]))), + <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a)*+(\\w)",[]))), + <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)*+(\\w)",[trim]))), <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)*+(\\w)",[{parts, - 2}]))), - <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)*+(\\w)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)*+(\\w)",[trim]))), + 2}]))), + <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)*+(\\w)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)*+(\\w)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)*+(\\w)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)*+(\\w)",[]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)*+(\\w)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)*+(\\w)",[]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)*+(\\w)",[trim]))), <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)*+(\\w)",[{parts, - 2}]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)*+(\\w)",[]))), - <<":X">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)*+(\\w)",[trim]))), + 2}]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)*+(\\w)",[]))), + <<":X">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)*+(\\w)",[trim]))), <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)*+(\\w)",[{parts, - 2}]))), - <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)*+(\\w)",[]))), - <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)*+(\\w)",[trim]))), + 2}]))), + <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)*+(\\w)",[]))), + <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)*+(\\w)",[trim]))), <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)*+(\\w)",[{parts, - 2}]))), - <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)*+(\\w)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)*+(\\w)",[trim]))), + 2}]))), + <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)*+(\\w)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)*+(\\w)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)*+(\\w)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)*+(\\w)",[]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)*+(\\w)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)*+(\\w)",[]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)*+(\\w)",[trim]))), <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)*+(\\w)",[{parts, - 2}]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)*+(\\w)",[]))), - <<":a:X">> = iolist_to_binary(join(re:split("aaaaX","^(a)++(\\w)",[trim]))), + 2}]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)*+(\\w)",[]))), + <<":a:X">> = iolist_to_binary(join(re:split("aaaaX","^(a)++(\\w)",[trim]))), <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a)++(\\w)",[{parts, - 2}]))), - <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a)++(\\w)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)++(\\w)",[trim]))), + 2}]))), + <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a)++(\\w)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)++(\\w)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)++(\\w)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)++(\\w)",[]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)++(\\w)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a)++(\\w)",[]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)++(\\w)",[trim]))), <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)++(\\w)",[{parts, - 2}]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)++(\\w)",[]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a)++(\\w)",[trim]))), + 2}]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(a)++(\\w)",[]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a)++(\\w)",[trim]))), <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a)++(\\w)",[{parts, - 2}]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a)++(\\w)",[]))), - <<":X">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)++(\\w)",[trim]))), + 2}]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a)++(\\w)",[]))), + <<":X">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)++(\\w)",[trim]))), <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)++(\\w)",[{parts, - 2}]))), - <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)++(\\w)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)++(\\w)",[trim]))), + 2}]))), + <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)++(\\w)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)++(\\w)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)++(\\w)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)++(\\w)",[]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)++(\\w)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a)++(\\w)",[]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)++(\\w)",[trim]))), <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)++(\\w)",[{parts, - 2}]))), - <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)++(\\w)",[]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a)++(\\w)",[trim]))), + 2}]))), + <<"aaaa">> = iolist_to_binary(join(re:split("aaaa","^(?:a)++(\\w)",[]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a)++(\\w)",[trim]))), <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a)++(\\w)",[{parts, - 2}]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a)++(\\w)",[]))), - <<":a:a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(a)?+(\\w)",[trim]))), + 2}]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a)++(\\w)",[]))), + <<":a:a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(a)?+(\\w)",[trim]))), <<":a:a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(a)?+(\\w)",[{parts, - 2}]))), - <<":a:a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(a)?+(\\w)",[]))), - <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)?+(\\w)",[trim]))), + 2}]))), + <<":a:a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(a)?+(\\w)",[]))), + <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)?+(\\w)",[trim]))), <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)?+(\\w)",[{parts, - 2}]))), - <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)?+(\\w)",[]))), - <<":a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)?+(\\w)",[trim]))), + 2}]))), + <<"::Y:Z">> = iolist_to_binary(join(re:split("YZ","^(a)?+(\\w)",[]))), + <<":a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)?+(\\w)",[trim]))), <<":a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)?+(\\w)",[{parts, - 2}]))), - <<":a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)?+(\\w)",[]))), - <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)?+(\\w)",[trim]))), + 2}]))), + <<":a:aaX">> = iolist_to_binary(join(re:split("aaaaX","^(?:a)?+(\\w)",[]))), + <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)?+(\\w)",[trim]))), <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)?+(\\w)",[{parts, - 2}]))), - <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)?+(\\w)",[]))), - <<":a:X">> = iolist_to_binary(join(re:split("aaaaX","^(a){2,}+(\\w)",[trim]))), + 2}]))), + <<":Y:Z">> = iolist_to_binary(join(re:split("YZ","^(?:a)?+(\\w)",[]))), + <<":a:X">> = iolist_to_binary(join(re:split("aaaaX","^(a){2,}+(\\w)",[trim]))), <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a){2,}+(\\w)",[{parts, - 2}]))), - <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a){2,}+(\\w)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a){2,}+(\\w)",[trim]))), + 2}]))), + <<":a:X:">> = iolist_to_binary(join(re:split("aaaaX","^(a){2,}+(\\w)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a){2,}+(\\w)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a){2,}+(\\w)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a){2,}+(\\w)",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a){2,}+(\\w)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(a){2,}+(\\w)",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a){2,}+(\\w)",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a){2,}+(\\w)",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a){2,}+(\\w)",[]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a){2,}+(\\w)",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(a){2,}+(\\w)",[]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a){2,}+(\\w)",[trim]))), <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a){2,}+(\\w)",[{parts, - 2}]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a){2,}+(\\w)",[]))), - <<":X">> = iolist_to_binary(join(re:split("aaaaX","^(?:a){2,}+(\\w)",[trim]))), + 2}]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(a){2,}+(\\w)",[]))), + <<":X">> = iolist_to_binary(join(re:split("aaaaX","^(?:a){2,}+(\\w)",[trim]))), <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a){2,}+(\\w)",[{parts, - 2}]))), - <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a){2,}+(\\w)",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a){2,}+(\\w)",[trim]))), + 2}]))), + <<":X:">> = iolist_to_binary(join(re:split("aaaaX","^(?:a){2,}+(\\w)",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a){2,}+(\\w)",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a){2,}+(\\w)",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a){2,}+(\\w)",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(?:a){2,}+(\\w)",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","^(?:a){2,}+(\\w)",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(?:a){2,}+(\\w)",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(?:a){2,}+(\\w)",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(?:a){2,}+(\\w)",[]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a){2,}+(\\w)",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^(?:a){2,}+(\\w)",[]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a){2,}+(\\w)",[trim]))), <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a){2,}+(\\w)",[{parts, - 2}]))), - <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a){2,}+(\\w)",[]))), - <<"">> = iolist_to_binary(join(re:split("b","(a|)*(?1)b",[trim]))), + 2}]))), + <<"YZ">> = iolist_to_binary(join(re:split("YZ","^(?:a){2,}+(\\w)",[]))), + <<"">> = iolist_to_binary(join(re:split("b","(a|)*(?1)b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","(a|)*(?1)b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","(a|)*(?1)b",[]))), - <<"">> = iolist_to_binary(join(re:split("ab","(a|)*(?1)b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","(a|)*(?1)b",[]))), + <<"">> = iolist_to_binary(join(re:split("ab","(a|)*(?1)b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("ab","(a|)*(?1)b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ab","(a|)*(?1)b",[]))), - <<"">> = iolist_to_binary(join(re:split("aab","(a|)*(?1)b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ab","(a|)*(?1)b",[]))), + <<"">> = iolist_to_binary(join(re:split("aab","(a|)*(?1)b",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aab","(a|)*(?1)b",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aab","(a|)*(?1)b",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)++(?1)b",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aab","(a|)*(?1)b",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)++(?1)b",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)++(?1)b",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)++(?1)b",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","(a)++(?1)b",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)++(?1)b",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","(a)++(?1)b",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","(a)++(?1)b",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","(a)++(?1)b",[]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","(a)++(?1)b",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","(a)++(?1)b",[]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","(a)++(?1)b",[trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","(a)++(?1)b",[{parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","(a)++(?1)b",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)*+(?1)b",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","(a)++(?1)b",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)*+(?1)b",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)*+(?1)b",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)*+(?1)b",[]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","(a)*+(?1)b",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(a)*+(?1)b",[]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","(a)*+(?1)b",[trim]))), <<"ab">> = iolist_to_binary(join(re:split("ab","(a)*+(?1)b",[{parts, - 2}]))), - <<"ab">> = iolist_to_binary(join(re:split("ab","(a)*+(?1)b",[]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","(a)*+(?1)b",[trim]))), + 2}]))), + <<"ab">> = iolist_to_binary(join(re:split("ab","(a)*+(?1)b",[]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","(a)*+(?1)b",[trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","(a)*+(?1)b",[{parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","(a)*+(?1)b",[]))), - <<"">> = iolist_to_binary(join(re:split("b","(?1)(?:(b)){0}",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","(a)*+(?1)b",[]))), + <<"">> = iolist_to_binary(join(re:split("b","(?1)(?:(b)){0}",[trim]))), <<"::">> = iolist_to_binary(join(re:split("b","(?1)(?:(b)){0}",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("b","(?1)(?:(b)){0}",[]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("b","(?1)(?:(b)){0}",[]))), <<":foo(bar(baz)+baz(bop)):(bar(baz)+baz(bop)):bar(baz)+baz(bop)">> = iolist_to_binary(join(re:split("foo(bar(baz)+baz(bop))","(foo ( \\( ((?:(?> [^()]+ )|(?2))*) \\) ) )",[extended, - trim]))), + trim]))), <<":foo(bar(baz)+baz(bop)):(bar(baz)+baz(bop)):bar(baz)+baz(bop):">> = iolist_to_binary(join(re:split("foo(bar(baz)+baz(bop))","(foo ( \\( ((?:(?> [^()]+ )|(?2))*) \\) ) )",[extended, {parts, - 2}]))), - <<":foo(bar(baz)+baz(bop)):(bar(baz)+baz(bop)):bar(baz)+baz(bop):">> = iolist_to_binary(join(re:split("foo(bar(baz)+baz(bop))","(foo ( \\( ((?:(?> [^()]+ )|(?2))*) \\) ) )",[extended]))), + 2}]))), + <<":foo(bar(baz)+baz(bop)):(bar(baz)+baz(bop)):bar(baz)+baz(bop):">> = iolist_to_binary(join(re:split("foo(bar(baz)+baz(bop))","(foo ( \\( ((?:(?> [^()]+ )|(?2))*) \\) ) )",[extended]))), <<":AB:B">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended, - trim]))), + trim]))), <<":AB:B::">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended, {parts, - 2}]))), - <<":AB:B::">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), + 2}]))), + <<":AB:B::">> = iolist_to_binary(join(re:split("AB","(A (A|B(*ACCEPT)|C) D)(E)",[extended]))), ok. run46() -> - <<":a">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)",[trim]))), + <<":a">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)",[]))), - <<"">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)++",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)",[]))), + <<"">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)++",[trim]))), <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)++",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)++",[]))), - <<":a">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)++",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)++",[]))), + <<":a">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)++",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)++",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)++",[]))), - <<"">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc|d)",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ba","\\A.*?(a|bc)++",[]))), + <<"">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc|d)",[trim]))), <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc|d)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc|d)",[]))), - <<":b:eetle">> = iolist_to_binary(join(re:split("beetle","(?:(b))++",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc|d)",[]))), + <<":b:eetle">> = iolist_to_binary(join(re:split("beetle","(?:(b))++",[trim]))), <<":b:eetle">> = iolist_to_binary(join(re:split("beetle","(?:(b))++",[{parts, - 2}]))), - <<":b:eetle">> = iolist_to_binary(join(re:split("beetle","(?:(b))++",[]))), - <<":a">> = iolist_to_binary(join(re:split("a","(?(?=(a(*ACCEPT)z))a)",[trim]))), + 2}]))), + <<":b:eetle">> = iolist_to_binary(join(re:split("beetle","(?:(b))++",[]))), + <<":a">> = iolist_to_binary(join(re:split("a","(?(?=(a(*ACCEPT)z))a)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("a","(?(?=(a(*ACCEPT)z))a)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("a","(?(?=(a(*ACCEPT)z))a)",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)+ab",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("a","(?(?=(a(*ACCEPT)z))a)",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)+ab",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)+ab",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)+ab",[]))), - <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)++ab",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)+ab",[]))), + <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)++ab",[trim]))), <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)++ab",[{parts, - 2}]))), - <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)++ab",[]))), - <<"::ckgammon">> = iolist_to_binary(join(re:split("backgammon","(?(DEFINE)(a))?b(?1)",[trim]))), + 2}]))), + <<"aaaab">> = iolist_to_binary(join(re:split("aaaab","^(a)(?1)++ab",[]))), + <<"::ckgammon">> = iolist_to_binary(join(re:split("backgammon","(?(DEFINE)(a))?b(?1)",[trim]))), <<"::ckgammon">> = iolist_to_binary(join(re:split("backgammon","(?(DEFINE)(a))?b(?1)",[{parts, - 2}]))), - <<"::ckgammon">> = iolist_to_binary(join(re:split("backgammon","(?(DEFINE)(a))?b(?1)",[]))), + 2}]))), + <<"::ckgammon">> = iolist_to_binary(join(re:split("backgammon","(?(DEFINE)(a))?b(?1)",[]))), <<": def">> = iolist_to_binary(join(re:split("abc -def","^\\N+",[trim]))), +def","^\\N+",[trim]))), <<": def">> = iolist_to_binary(join(re:split("abc -def","^\\N+",[{parts,2}]))), +def","^\\N+",[{parts,2}]))), <<": def">> = iolist_to_binary(join(re:split("abc -def","^\\N+",[]))), +def","^\\N+",[]))), <<": def">> = iolist_to_binary(join(re:split("abc -def","^\\N{1,}",[trim]))), +def","^\\N{1,}",[trim]))), <<": def">> = iolist_to_binary(join(re:split("abc -def","^\\N{1,}",[{parts,2}]))), +def","^\\N{1,}",[{parts,2}]))), <<": def">> = iolist_to_binary(join(re:split("abc -def","^\\N{1,}",[]))), - <<":cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|(?R)b)",[trim]))), +def","^\\N{1,}",[]))), + <<":cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|(?R)b)",[trim]))), <<":cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|(?R)b)",[{parts, - 2}]))), - <<":cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|(?R)b)",[]))), - <<":aaaa:cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|((?R))b)",[trim]))), + 2}]))), + <<":cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|(?R)b)",[]))), + <<":aaaa:cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|((?R))b)",[trim]))), <<":aaaa:cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|((?R))b)",[{parts, - 2}]))), - <<":aaaa:cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|((?R))b)",[]))), - <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R)a+|(?1)b))",[trim]))), + 2}]))), + <<":aaaa:cde">> = iolist_to_binary(join(re:split("aaaabcde","(?(R)a+|((?R))b)",[]))), + <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R)a+|(?1)b))",[trim]))), <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R)a+|(?1)b))",[{parts, - 2}]))), - <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R)a+|(?1)b))",[]))), - <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R1)a+|(?1)b))",[trim]))), + 2}]))), + <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R)a+|(?1)b))",[]))), + <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R1)a+|(?1)b))",[trim]))), <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R1)a+|(?1)b))",[{parts, - 2}]))), - <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R1)a+|(?1)b))",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))*",[trim]))), + 2}]))), + <<":aaaab:cde">> = iolist_to_binary(join(re:split("aaaabcde","((?(R1)a+|(?1)b))",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))*",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))*",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))*",[]))), - <<":a">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))+",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))*",[]))), + <<":a">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))+",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))+",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))+",[]))), - <<"">> = iolist_to_binary(join(re:split("a","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("aaa","((?(R)a|(?1)))+",[]))), + <<"">> = iolist_to_binary(join(re:split("a","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[trim]))), <<"::">> = iolist_to_binary(join(re:split("a","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("a","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[]))), - <<"b">> = iolist_to_binary(join(re:split("ba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("a","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[]))), + <<"b">> = iolist_to_binary(join(re:split("ba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[trim]))), <<"b::">> = iolist_to_binary(join(re:split("ba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[{parts, - 2}]))), - <<"b::">> = iolist_to_binary(join(re:split("ba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[]))), - <<"bb">> = iolist_to_binary(join(re:split("bba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[trim]))), + 2}]))), + <<"b::">> = iolist_to_binary(join(re:split("ba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[]))), + <<"bb">> = iolist_to_binary(join(re:split("bba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[trim]))), <<"bb::">> = iolist_to_binary(join(re:split("bba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[{parts, - 2}]))), - <<"bb::">> = iolist_to_binary(join(re:split("bba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[]))), + 2}]))), + <<"bb::">> = iolist_to_binary(join(re:split("bba","(?>(?&t)c|(?&t))(?(DEFINE)(?<t>a|b(*PRUNE)c))",[]))), ok. run47() -> <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b) c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b) c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b) c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b) c",[extended]))), <<":ab">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F)) c",[extended, - trim]))), + trim]))), <<":ab:">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F)) c",[extended, {parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F)) c",[extended]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F)) c",[extended]))), <<":ab:ab">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) | (*F) ) c",[extended, - trim]))), + trim]))), <<":ab:ab:">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) | (*F) ) c",[extended, {parts, - 2}]))), - <<":ab:ab:">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) | (*F) ) c",[extended]))), + 2}]))), + <<":ab:ab:">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) | (*F) ) c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) ) c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) ) c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) ) c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b) ) c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b) c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b) c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b) c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b) c",[extended]))), <<"">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F)) c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F)) c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F)) c",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F)) c",[extended]))), <<"">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) | (*F) ) c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) | (*F) ) c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) | (*F) ) c",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) | (*F) ) c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) ) c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) ) c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) ) c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b) ) c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b) c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b) c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b) c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b) c",[extended]))), <<"">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b|(*F)) c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b|(*F)) c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b|(*F)) c",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?>a(*THEN)b|(*F)) c",[extended]))), <<"">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) | (*F) ) c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) | (*F) ) c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) | (*F) ) c",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) | (*F) ) c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) ) c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) ) c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) ) c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?> (?>a(*THEN)b) ) c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b)++ c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b)++ c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b)++ c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b)++ c",[extended]))), <<":ab">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F))++ c",[extended, - trim]))), + trim]))), <<":ab:">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F))++ c",[extended, {parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F))++ c",[extended]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("aabc","^.*? (a(*THEN)b|(*F))++ c",[extended]))), <<":ab:ab">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ | (*F) )++ c",[extended, - trim]))), + trim]))), <<":ab:ab:">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ | (*F) )++ c",[extended, {parts, - 2}]))), - <<":ab:ab:">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ | (*F) )++ c",[extended]))), + 2}]))), + <<":ab:ab:">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ | (*F) )++ c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ )++ c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ )++ c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ )++ c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? ( (a(*THEN)b)++ )++ c",[extended]))), ok. run48() -> <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b)++ c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b)++ c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b)++ c",[extended]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b)++ c",[extended]))), <<"">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F))++ c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F))++ c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F))++ c",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?:a(*THEN)b|(*F))++ c",[extended]))), <<"">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ | (*F) )++ c",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ | (*F) )++ c",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ | (*F) )++ c",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ | (*F) )++ c",[extended]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ )++ c",[extended, - trim]))), + trim]))), <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ )++ c",[extended, {parts, - 2}]))), - <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ )++ c",[extended]))), - <<"">> = iolist_to_binary(join(re:split("ac","^(?(?=a(*THEN)b)ab|ac)",[trim]))), + 2}]))), + <<"aabc">> = iolist_to_binary(join(re:split("aabc","^.*? (?: (?:a(*THEN)b)++ )++ c",[extended]))), + <<"">> = iolist_to_binary(join(re:split("ac","^(?(?=a(*THEN)b)ab|ac)",[trim]))), <<":">> = iolist_to_binary(join(re:split("ac","^(?(?=a(*THEN)b)ab|ac)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ac","^(?(?=a(*THEN)b)ab|ac)",[]))), - <<"ba">> = iolist_to_binary(join(re:split("ba","^.*?(?(?=a)a|b(*THEN)c)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ac","^(?(?=a(*THEN)b)ab|ac)",[]))), + <<"ba">> = iolist_to_binary(join(re:split("ba","^.*?(?(?=a)a|b(*THEN)c)",[trim]))), <<"ba">> = iolist_to_binary(join(re:split("ba","^.*?(?(?=a)a|b(*THEN)c)",[{parts, - 2}]))), - <<"ba">> = iolist_to_binary(join(re:split("ba","^.*?(?(?=a)a|b(*THEN)c)",[]))), - <<"">> = iolist_to_binary(join(re:split("ba","^.*?(?:(?(?=a)a|b(*THEN)c)|d)",[trim]))), + 2}]))), + <<"ba">> = iolist_to_binary(join(re:split("ba","^.*?(?(?=a)a|b(*THEN)c)",[]))), + <<"">> = iolist_to_binary(join(re:split("ba","^.*?(?:(?(?=a)a|b(*THEN)c)|d)",[trim]))), <<":">> = iolist_to_binary(join(re:split("ba","^.*?(?:(?(?=a)a|b(*THEN)c)|d)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ba","^.*?(?:(?(?=a)a|b(*THEN)c)|d)",[]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^.*?(?(?=a)a(*THEN)b|c)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ba","^.*?(?:(?(?=a)a|b(*THEN)c)|d)",[]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^.*?(?(?=a)a(*THEN)b|c)",[trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","^.*?(?(?=a)a(*THEN)b|c)",[{parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^.*?(?(?=a)a(*THEN)b|c)",[]))), - <<":abc">> = iolist_to_binary(join(re:split("aabc","^.*(?=a(*THEN)b)",[trim]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^.*?(?(?=a)a(*THEN)b|c)",[]))), + <<":abc">> = iolist_to_binary(join(re:split("aabc","^.*(?=a(*THEN)b)",[trim]))), <<":abc">> = iolist_to_binary(join(re:split("aabc","^.*(?=a(*THEN)b)",[{parts, - 2}]))), - <<":abc">> = iolist_to_binary(join(re:split("aabc","^.*(?=a(*THEN)b)",[]))), - <<"xa:d">> = iolist_to_binary(join(re:split("xacd","(?<=a(*ACCEPT)b)c",[trim]))), + 2}]))), + <<":abc">> = iolist_to_binary(join(re:split("aabc","^.*(?=a(*THEN)b)",[]))), + <<"xa:d">> = iolist_to_binary(join(re:split("xacd","(?<=a(*ACCEPT)b)c",[trim]))), <<"xa:d">> = iolist_to_binary(join(re:split("xacd","(?<=a(*ACCEPT)b)c",[{parts, - 2}]))), - <<"xa:d">> = iolist_to_binary(join(re:split("xacd","(?<=a(*ACCEPT)b)c",[]))), - <<"xa:a:d">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*ACCEPT)b))c",[trim]))), + 2}]))), + <<"xa:d">> = iolist_to_binary(join(re:split("xacd","(?<=a(*ACCEPT)b)c",[]))), + <<"xa:a:d">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*ACCEPT)b))c",[trim]))), <<"xa:a:d">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*ACCEPT)b))c",[{parts, - 2}]))), - <<"xa:a:d">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*ACCEPT)b))c",[]))), - <<"xab:ab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=(a(*COMMIT)b))c",[trim]))), + 2}]))), + <<"xa:a:d">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*ACCEPT)b))c",[]))), + <<"xab:ab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=(a(*COMMIT)b))c",[trim]))), <<"xab:ab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=(a(*COMMIT)b))c",[{parts, - 2}]))), - <<"xab:ab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=(a(*COMMIT)b))c",[]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=(a(*COMMIT)b))c",[trim]))), + 2}]))), + <<"xab:ab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=(a(*COMMIT)b))c",[]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=(a(*COMMIT)b))c",[trim]))), <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=(a(*COMMIT)b))c",[{parts, - 2}]))), - <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=(a(*COMMIT)b))c",[]))), - <<"xacd">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*COMMIT)b))c",[trim]))), + 2}]))), + <<"** Failers">> = iolist_to_binary(join(re:split("** Failers","(?<=(a(*COMMIT)b))c",[]))), + <<"xacd">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*COMMIT)b))c",[trim]))), <<"xacd">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*COMMIT)b))c",[{parts, - 2}]))), - <<"xacd">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*COMMIT)b))c",[]))), - <<"x:d">> = iolist_to_binary(join(re:split("xcd","(?<!a(*FAIL)b)c",[trim]))), + 2}]))), + <<"xacd">> = iolist_to_binary(join(re:split("xacd","(?<=(a(*COMMIT)b))c",[]))), + <<"x:d">> = iolist_to_binary(join(re:split("xcd","(?<!a(*FAIL)b)c",[trim]))), <<"x:d">> = iolist_to_binary(join(re:split("xcd","(?<!a(*FAIL)b)c",[{parts, - 2}]))), - <<"x:d">> = iolist_to_binary(join(re:split("xcd","(?<!a(*FAIL)b)c",[]))), - <<"a:d">> = iolist_to_binary(join(re:split("acd","(?<!a(*FAIL)b)c",[trim]))), + 2}]))), + <<"x:d">> = iolist_to_binary(join(re:split("xcd","(?<!a(*FAIL)b)c",[]))), + <<"a:d">> = iolist_to_binary(join(re:split("acd","(?<!a(*FAIL)b)c",[trim]))), <<"a:d">> = iolist_to_binary(join(re:split("acd","(?<!a(*FAIL)b)c",[{parts, - 2}]))), - <<"a:d">> = iolist_to_binary(join(re:split("acd","(?<!a(*FAIL)b)c",[]))), - <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*PRUNE)b)c",[trim]))), + 2}]))), + <<"a:d">> = iolist_to_binary(join(re:split("acd","(?<!a(*FAIL)b)c",[]))), + <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*PRUNE)b)c",[trim]))), <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*PRUNE)b)c",[{parts, - 2}]))), - <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*PRUNE)b)c",[]))), - <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*SKIP)b)c",[trim]))), + 2}]))), + <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*PRUNE)b)c",[]))), + <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*SKIP)b)c",[trim]))), <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*SKIP)b)c",[{parts, - 2}]))), - <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*SKIP)b)c",[]))), - <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*THEN)b)c",[trim]))), + 2}]))), + <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*SKIP)b)c",[]))), + <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*THEN)b)c",[trim]))), <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*THEN)b)c",[{parts, - 2}]))), - <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*THEN)b)c",[]))), + 2}]))), + <<"xab:d">> = iolist_to_binary(join(re:split("xabcd","(?<=a(*THEN)b)c",[]))), ok. run49() -> - <<":a:d">> = iolist_to_binary(join(re:split("abcd","(a)(?2){2}(.)",[trim]))), + <<":a:d">> = iolist_to_binary(join(re:split("abcd","(a)(?2){2}(.)",[trim]))), <<":a:d:">> = iolist_to_binary(join(re:split("abcd","(a)(?2){2}(.)",[{parts, - 2}]))), - <<":a:d:">> = iolist_to_binary(join(re:split("abcd","(a)(?2){2}(.)",[]))), - <<"hello world ">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1?)test",[trim]))), + 2}]))), + <<":a:d:">> = iolist_to_binary(join(re:split("abcd","(a)(?2){2}(.)",[]))), + <<"hello world ">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1?)test",[trim]))), <<"hello world :::">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1?)test",[{parts, - 2}]))), - <<"hello world :::">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1?)test",[]))), - <<"hello world test">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1+)test",[trim]))), + 2}]))), + <<"hello world :::">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1?)test",[]))), + <<"hello world test">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1+)test",[trim]))), <<"hello world test">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1+)test",[{parts, - 2}]))), - <<"hello world test">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1+)test",[]))), - <<"">> = iolist_to_binary(join(re:split("aac","(a(*COMMIT)b){0}a(?1)|aac",[trim]))), + 2}]))), + <<"hello world test">> = iolist_to_binary(join(re:split("hello world test","(another)?(\\1+)test",[]))), + <<"">> = iolist_to_binary(join(re:split("aac","(a(*COMMIT)b){0}a(?1)|aac",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aac","(a(*COMMIT)b){0}a(?1)|aac",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aac","(a(*COMMIT)b){0}a(?1)|aac",[]))), - <<"">> = iolist_to_binary(join(re:split("aac","((?:a?)*)*c",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aac","(a(*COMMIT)b){0}a(?1)|aac",[]))), + <<"">> = iolist_to_binary(join(re:split("aac","((?:a?)*)*c",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aac","((?:a?)*)*c",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aac","((?:a?)*)*c",[]))), - <<"">> = iolist_to_binary(join(re:split("aac","((?>a?)*)*c",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aac","((?:a?)*)*c",[]))), + <<"">> = iolist_to_binary(join(re:split("aac","((?>a?)*)*c",[trim]))), <<"::">> = iolist_to_binary(join(re:split("aac","((?>a?)*)*c",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("aac","((?>a?)*)*c",[]))), - <<"a">> = iolist_to_binary(join(re:split("aba","(?>.*?a)(?<=ba)",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("aac","((?>a?)*)*c",[]))), + <<"a">> = iolist_to_binary(join(re:split("aba","(?>.*?a)(?<=ba)",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aba","(?>.*?a)(?<=ba)",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aba","(?>.*?a)(?<=ba)",[]))), - <<"">> = iolist_to_binary(join(re:split("aba","(?:.*?a)(?<=ba)",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aba","(?>.*?a)(?<=ba)",[]))), + <<"">> = iolist_to_binary(join(re:split("aba","(?:.*?a)(?<=ba)",[trim]))), <<":">> = iolist_to_binary(join(re:split("aba","(?:.*?a)(?<=ba)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aba","(?:.*?a)(?<=ba)",[]))), - <<"a">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aba","(?:.*?a)(?<=ba)",[]))), + <<"a">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[]))), <<"a">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[dotall, - trim]))), + trim]))), <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[dotall, {parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[dotall]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*PRUNE)b",[dotall]))), <<"aab">> = iolist_to_binary(join(re:split("aab","^a(*PRUNE)b",[dotall, - trim]))), + trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","^a(*PRUNE)b",[dotall, {parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","^a(*PRUNE)b",[dotall]))), - <<"a">> = iolist_to_binary(join(re:split("aab",".*?a(*SKIP)b",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","^a(*PRUNE)b",[dotall]))), + <<"a">> = iolist_to_binary(join(re:split("aab",".*?a(*SKIP)b",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*SKIP)b",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*SKIP)b",[]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aab",".*?a(*SKIP)b",[]))), <<"a">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[dotall, - trim]))), + trim]))), <<"a:">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[dotall, {parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[dotall]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[dotall]))), ok. run50() -> - <<"a">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[trim]))), + <<"a">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("aab","(?>.*?a)b",[]))), <<"aab">> = iolist_to_binary(join(re:split("aab","(?>^a)b",[dotall, - trim]))), + trim]))), <<"aab">> = iolist_to_binary(join(re:split("aab","(?>^a)b",[dotall, {parts, - 2}]))), - <<"aab">> = iolist_to_binary(join(re:split("aab","(?>^a)b",[dotall]))), - <<"alphabetabcd:abcd">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*?)(?<=(abcd)|(wxyz))",[trim]))), + 2}]))), + <<"aab">> = iolist_to_binary(join(re:split("aab","(?>^a)b",[dotall]))), + <<"alphabetabcd:abcd">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*?)(?<=(abcd)|(wxyz))",[trim]))), <<"alphabetabcd:abcd::">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*?)(?<=(abcd)|(wxyz))",[{parts, - 2}]))), - <<"alphabetabcd:abcd::">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*?)(?<=(abcd)|(wxyz))",[]))), - <<"endingwxyz::wxyz">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*?)(?<=(abcd)|(wxyz))",[trim]))), + 2}]))), + <<"alphabetabcd:abcd::">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*?)(?<=(abcd)|(wxyz))",[]))), + <<"endingwxyz::wxyz">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*?)(?<=(abcd)|(wxyz))",[trim]))), <<"endingwxyz::wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*?)(?<=(abcd)|(wxyz))",[{parts, - 2}]))), - <<"endingwxyz::wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*?)(?<=(abcd)|(wxyz))",[]))), - <<":abcd">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd)|(wxyz))",[trim]))), + 2}]))), + <<"endingwxyz::wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*?)(?<=(abcd)|(wxyz))",[]))), + <<":abcd">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd)|(wxyz))",[trim]))), <<":abcd::">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd)|(wxyz))",[{parts, - 2}]))), - <<":abcd::">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd)|(wxyz))",[]))), - <<"::wxyz">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd)|(wxyz))",[trim]))), + 2}]))), + <<":abcd::">> = iolist_to_binary(join(re:split("alphabetabcd","(?>.*)(?<=(abcd)|(wxyz))",[]))), + <<"::wxyz">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd)|(wxyz))",[trim]))), <<"::wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd)|(wxyz))",[{parts, - 2}]))), - <<"::wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd)|(wxyz))",[]))), - <<"abcdfooxyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*)foo",[trim]))), + 2}]))), + <<"::wxyz:">> = iolist_to_binary(join(re:split("endingwxyz","(?>.*)(?<=(abcd)|(wxyz))",[]))), + <<"abcdfooxyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*)foo",[trim]))), <<"abcdfooxyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*)foo",[{parts, - 2}]))), - <<"abcdfooxyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*)foo",[]))), - <<"abcd:xyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*?)foo",[trim]))), + 2}]))), + <<"abcdfooxyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*)foo",[]))), + <<"abcd:xyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*?)foo",[trim]))), <<"abcd:xyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*?)foo",[{parts, - 2}]))), - <<"abcd:xyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*?)foo",[]))), - <<"">> = iolist_to_binary(join(re:split("ac","(?:(a(*PRUNE)b)){0}(?:(?1)|ac)",[trim]))), + 2}]))), + <<"abcd:xyz">> = iolist_to_binary(join(re:split("abcdfooxyz","(?>.*?)foo",[]))), + <<"">> = iolist_to_binary(join(re:split("ac","(?:(a(*PRUNE)b)){0}(?:(?1)|ac)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("ac","(?:(a(*PRUNE)b)){0}(?:(?1)|ac)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ac","(?:(a(*PRUNE)b)){0}(?:(?1)|ac)",[]))), - <<"">> = iolist_to_binary(join(re:split("ac","(?:(a(*SKIP)b)){0}(?:(?1)|ac)",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ac","(?:(a(*PRUNE)b)){0}(?:(?1)|ac)",[]))), + <<"">> = iolist_to_binary(join(re:split("ac","(?:(a(*SKIP)b)){0}(?:(?1)|ac)",[trim]))), <<"::">> = iolist_to_binary(join(re:split("ac","(?:(a(*SKIP)b)){0}(?:(?1)|ac)",[{parts, - 2}]))), - <<"::">> = iolist_to_binary(join(re:split("ac","(?:(a(*SKIP)b)){0}(?:(?1)|ac)",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","(?<=(*SKIP)ac)a",[trim]))), + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("ac","(?:(a(*SKIP)b)){0}(?:(?1)|ac)",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","(?<=(*SKIP)ac)a",[trim]))), <<"aa">> = iolist_to_binary(join(re:split("aa","(?<=(*SKIP)ac)a",[{parts, - 2}]))), - <<"aa">> = iolist_to_binary(join(re:split("aa","(?<=(*SKIP)ac)a",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)b|a+c",[trim]))), + 2}]))), + <<"aa">> = iolist_to_binary(join(re:split("aa","(?<=(*SKIP)ac)a",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)b|a+c",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*PRUNE)b|a+c",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)b|a+c",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*PRUNE)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*PRUNE)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*PRUNE)b|a+c",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP:N)(*PRUNE)b|a+c",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*PRUNE)b|a+c",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP:N)(*PRUNE)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP:N)(*PRUNE)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP:N)(*PRUNE)b|a+c",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaa(*:N)a(*SKIP:N)(*PRUNE)b|a+c",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP:N)(*PRUNE)b|a+c",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaa(*:N)a(*SKIP:N)(*PRUNE)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaa(*:N)a(*SKIP:N)(*PRUNE)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaa(*:N)a(*SKIP:N)(*PRUNE)b|a+c",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*PRUNE)b|a+c",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaa(*:N)a(*SKIP:N)(*PRUNE)b|a+c",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*PRUNE)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*PRUNE)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*PRUNE)b|a+c",[]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*PRUNE)b|a+c",[]))), ok. run51() -> - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)b|a+c",[trim]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)b|a+c",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)b|a+c",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)b|a+c",[]))), - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*SKIP)b|a+c",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)b|a+c",[]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*SKIP)b|a+c",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*SKIP)b|a+c",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*SKIP)b|a+c",[]))), - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*SKIP)b|a+c",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*SKIP)b|a+c",[]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*SKIP)b|a+c",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*SKIP)b|a+c",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*SKIP)b|a+c",[]))), - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*SKIP)b|a+c",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)(*SKIP)b|a+c",[]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*SKIP)b|a+c",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*SKIP)b|a+c",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*SKIP)b|a+c",[]))), - <<"aaaaaac">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)b|a+c",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*SKIP)b|a+c",[]))), + <<"aaaaaac">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)b|a+c",[trim]))), <<"aaaaaac">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)b|a+c",[{parts, - 2}]))), - <<"aaaaaac">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)b|a+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)b|a+c",[trim]))), + 2}]))), + <<"aaaaaac">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)b|a+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)b|a+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)b|a+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)b|a+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*THEN)b|a+c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*THEN)b|a+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*THEN)b|a+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*THEN)b|a+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*THEN)b|a+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*THEN)b|a+c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*SKIP)(*THEN)b|a+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*THEN)b|a+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*THEN)b|a+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*THEN)b|a+c",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*THEN)b|a+c",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*PRUNE)(*THEN)b|a+c",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*THEN)b|a+c",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*THEN)b|a+c",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*THEN)b|a+c",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaaaac","aaaaa(*COMMIT)(*THEN)b|a+c",[]))), ok. run52() -> - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*PRUNE:m)(*SKIP:m)m|a+",[trim]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*PRUNE:m)(*SKIP:m)m|a+",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*PRUNE:m)(*SKIP:m)m|a+",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*PRUNE:m)(*SKIP:m)m|a+",[]))), - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*PRUNE:m)(*SKIP:m)m|a+",[]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*PRUNE:m)(*SKIP:m)m|a+",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:m)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*PRUNE:m)(*SKIP:m)m|a+",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*PRUNE:m)(*SKIP:m)m|a+",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*PRUNE:m)(*SKIP:m)m|a+",[]))), - <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*PRUNE:m)(*SKIP:m)m|a+",[]))), + <<"aaaaa">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[trim]))), <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[{parts, - 2}]))), - <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*PRUNE:A)a(*SKIP:A)b|a+c",[trim]))), + 2}]))), + <<"aaaaa:">> = iolist_to_binary(join(re:split("aaaaaa","aaaaa(*:n)(*MARK:m)(*PRUNE)(*SKIP:m)m|a+",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*PRUNE:A)a(*SKIP:A)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*PRUNE:A)a(*SKIP:A)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*PRUNE:A)a(*SKIP:A)b|a+c",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*MARK:A)a(*SKIP:A)b|a+c",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*PRUNE:A)a(*SKIP:A)b|a+c",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*MARK:A)a(*SKIP:A)b|a+c",[trim]))), <<"aaa:">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*MARK:A)a(*SKIP:A)b|a+c",[{parts, - 2}]))), - <<"aaa:">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*MARK:A)a(*SKIP:A)b|a+c",[]))), - <<"aa">> = iolist_to_binary(join(re:split("aaaac","aaa(*PRUNE:A)a(*SKIP:A)b|a+c",[trim]))), + 2}]))), + <<"aaa:">> = iolist_to_binary(join(re:split("aaaac","a(*MARK:A)aa(*MARK:A)a(*SKIP:A)b|a+c",[]))), + <<"aa">> = iolist_to_binary(join(re:split("aaaac","aaa(*PRUNE:A)a(*SKIP:A)b|a+c",[trim]))), <<"aa:">> = iolist_to_binary(join(re:split("aaaac","aaa(*PRUNE:A)a(*SKIP:A)b|a+c",[{parts, - 2}]))), - <<"aa:">> = iolist_to_binary(join(re:split("aaaac","aaa(*PRUNE:A)a(*SKIP:A)b|a+c",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaaac","aaa(*MARK:A)a(*SKIP:A)b|a+c",[trim]))), + 2}]))), + <<"aa:">> = iolist_to_binary(join(re:split("aaaac","aaa(*PRUNE:A)a(*SKIP:A)b|a+c",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaaac","aaa(*MARK:A)a(*SKIP:A)b|a+c",[trim]))), <<"aaa:">> = iolist_to_binary(join(re:split("aaaac","aaa(*MARK:A)a(*SKIP:A)b|a+c",[{parts, - 2}]))), - <<"aaa:">> = iolist_to_binary(join(re:split("aaaac","aaa(*MARK:A)a(*SKIP:A)b|a+c",[]))), - <<":a">> = iolist_to_binary(join(re:split("ba",".?(a|b(*THEN)c)",[trim]))), + 2}]))), + <<"aaa:">> = iolist_to_binary(join(re:split("aaaac","aaa(*MARK:A)a(*SKIP:A)b|a+c",[]))), + <<":a">> = iolist_to_binary(join(re:split("ba",".?(a|b(*THEN)c)",[trim]))), <<":a:">> = iolist_to_binary(join(re:split("ba",".?(a|b(*THEN)c)",[{parts, - 2}]))), - <<":a:">> = iolist_to_binary(join(re:split("ba",".?(a|b(*THEN)c)",[]))), - <<":ab">> = iolist_to_binary(join(re:split("abc","(a(*COMMIT)b)c|abd",[trim]))), + 2}]))), + <<":a:">> = iolist_to_binary(join(re:split("ba",".?(a|b(*THEN)c)",[]))), + <<":ab">> = iolist_to_binary(join(re:split("abc","(a(*COMMIT)b)c|abd",[trim]))), <<":ab:">> = iolist_to_binary(join(re:split("abc","(a(*COMMIT)b)c|abd",[{parts, - 2}]))), - <<":ab:">> = iolist_to_binary(join(re:split("abc","(a(*COMMIT)b)c|abd",[]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","(a(*COMMIT)b)c|abd",[trim]))), + 2}]))), + <<":ab:">> = iolist_to_binary(join(re:split("abc","(a(*COMMIT)b)c|abd",[]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","(a(*COMMIT)b)c|abd",[trim]))), <<"abd">> = iolist_to_binary(join(re:split("abd","(a(*COMMIT)b)c|abd",[{parts, - 2}]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","(a(*COMMIT)b)c|abd",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","(?=a(*COMMIT)b)abc|abd",[trim]))), + 2}]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","(a(*COMMIT)b)c|abd",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","(?=a(*COMMIT)b)abc|abd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","(?=a(*COMMIT)b)abc|abd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","(?=a(*COMMIT)b)abc|abd",[]))), - <<"">> = iolist_to_binary(join(re:split("abd","(?=a(*COMMIT)b)abc|abd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","(?=a(*COMMIT)b)abc|abd",[]))), + <<"">> = iolist_to_binary(join(re:split("abd","(?=a(*COMMIT)b)abc|abd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abd","(?=a(*COMMIT)b)abc|abd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abd","(?=a(*COMMIT)b)abc|abd",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","(?>a(*COMMIT)b)c|abd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abd","(?=a(*COMMIT)b)abc|abd",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","(?>a(*COMMIT)b)c|abd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","(?>a(*COMMIT)b)c|abd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","(?>a(*COMMIT)b)c|abd",[]))), - <<"">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","(?>a(*COMMIT)b)c|abd",[]))), + <<"">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","a(?=b(*COMMIT)c)[^d]|abd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","a(?=b(*COMMIT)c)[^d]|abd",[trim]))), <<"abd">> = iolist_to_binary(join(re:split("abd","a(?=b(*COMMIT)c)[^d]|abd",[{parts, - 2}]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","a(?=b(*COMMIT)c)[^d]|abd",[]))), - <<":c">> = iolist_to_binary(join(re:split("abc","a(?=b(*COMMIT)c)[^d]|abd",[trim]))), + 2}]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","a(?=b(*COMMIT)c)[^d]|abd",[]))), + <<":c">> = iolist_to_binary(join(re:split("abc","a(?=b(*COMMIT)c)[^d]|abd",[trim]))), <<":c">> = iolist_to_binary(join(re:split("abc","a(?=b(*COMMIT)c)[^d]|abd",[{parts, - 2}]))), - <<":c">> = iolist_to_binary(join(re:split("abc","a(?=b(*COMMIT)c)[^d]|abd",[]))), - <<"">> = iolist_to_binary(join(re:split("abd","a(?=bc).|abd",[trim]))), + 2}]))), + <<":c">> = iolist_to_binary(join(re:split("abc","a(?=b(*COMMIT)c)[^d]|abd",[]))), + <<"">> = iolist_to_binary(join(re:split("abd","a(?=bc).|abd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abd","a(?=bc).|abd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abd","a(?=bc).|abd",[]))), - <<":c">> = iolist_to_binary(join(re:split("abc","a(?=bc).|abd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abd","a(?=bc).|abd",[]))), + <<":c">> = iolist_to_binary(join(re:split("abc","a(?=bc).|abd",[trim]))), <<":c">> = iolist_to_binary(join(re:split("abc","a(?=bc).|abd",[{parts, - 2}]))), - <<":c">> = iolist_to_binary(join(re:split("abc","a(?=bc).|abd",[]))), - <<"abceabd">> = iolist_to_binary(join(re:split("abceabd","a(?>b(*COMMIT)c)d|abd",[trim]))), + 2}]))), + <<":c">> = iolist_to_binary(join(re:split("abc","a(?=bc).|abd",[]))), + <<"abceabd">> = iolist_to_binary(join(re:split("abceabd","a(?>b(*COMMIT)c)d|abd",[trim]))), <<"abceabd">> = iolist_to_binary(join(re:split("abceabd","a(?>b(*COMMIT)c)d|abd",[{parts, - 2}]))), - <<"abceabd">> = iolist_to_binary(join(re:split("abceabd","a(?>b(*COMMIT)c)d|abd",[]))), - <<"abce">> = iolist_to_binary(join(re:split("abceabd","a(?>bc)d|abd",[trim]))), + 2}]))), + <<"abceabd">> = iolist_to_binary(join(re:split("abceabd","a(?>b(*COMMIT)c)d|abd",[]))), + <<"abce">> = iolist_to_binary(join(re:split("abceabd","a(?>bc)d|abd",[trim]))), <<"abce:">> = iolist_to_binary(join(re:split("abceabd","a(?>bc)d|abd",[{parts, - 2}]))), - <<"abce:">> = iolist_to_binary(join(re:split("abceabd","a(?>bc)d|abd",[]))), - <<"">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[trim]))), + 2}]))), + <<"abce:">> = iolist_to_binary(join(re:split("abceabd","a(?>bc)d|abd",[]))), + <<"">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[trim]))), <<":">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)c)d|abd",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)b)c|abd",[]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)c)d|abd",[trim]))), <<"abd">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)c)d|abd",[{parts, - 2}]))), - <<"abd">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)c)d|abd",[]))), - <<"::c">> = iolist_to_binary(join(re:split("ac","((?=a(*COMMIT)b)ab|ac){0}(?:(?1)|a(c))",[trim]))), + 2}]))), + <<"abd">> = iolist_to_binary(join(re:split("abd","(?>a(*COMMIT)c)d|abd",[]))), + <<"::c">> = iolist_to_binary(join(re:split("ac","((?=a(*COMMIT)b)ab|ac){0}(?:(?1)|a(c))",[trim]))), <<"::c:">> = iolist_to_binary(join(re:split("ac","((?=a(*COMMIT)b)ab|ac){0}(?:(?1)|a(c))",[{parts, - 2}]))), - <<"::c:">> = iolist_to_binary(join(re:split("ac","((?=a(*COMMIT)b)ab|ac){0}(?:(?1)|a(c))",[]))), + 2}]))), + <<"::c:">> = iolist_to_binary(join(re:split("ac","((?=a(*COMMIT)b)ab|ac){0}(?:(?1)|a(c))",[]))), ok. run53() -> - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[trim]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(a)?(?(1)a|b)+$",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^(a)?(?(1)a|b)+$",[]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(a)?(?(1)a|b)+$",[trim]))), <<"a">> = iolist_to_binary(join(re:split("a","^(a)?(?(1)a|b)+$",[{parts, - 2}]))), - <<"a">> = iolist_to_binary(join(re:split("a","^(a)?(?(1)a|b)+$",[]))), - <<"a">> = iolist_to_binary(join(re:split("ab","(?=a\\Kb)ab",[trim]))), + 2}]))), + <<"a">> = iolist_to_binary(join(re:split("a","^(a)?(?(1)a|b)+$",[]))), + <<"a">> = iolist_to_binary(join(re:split("ab","(?=a\\Kb)ab",[trim]))), <<"a:">> = iolist_to_binary(join(re:split("ab","(?=a\\Kb)ab",[{parts, - 2}]))), - <<"a:">> = iolist_to_binary(join(re:split("ab","(?=a\\Kb)ab",[]))), - <<"">> = iolist_to_binary(join(re:split("ac","(?!a\\Kb)ac",[trim]))), + 2}]))), + <<"a:">> = iolist_to_binary(join(re:split("ab","(?=a\\Kb)ab",[]))), + <<"">> = iolist_to_binary(join(re:split("ac","(?!a\\Kb)ac",[trim]))), <<":">> = iolist_to_binary(join(re:split("ac","(?!a\\Kb)ac",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ac","(?!a\\Kb)ac",[]))), - <<"ab">> = iolist_to_binary(join(re:split("abcd","^abc(?<=b\\Kc)d",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ac","(?!a\\Kb)ac",[]))), + <<"ab">> = iolist_to_binary(join(re:split("abcd","^abc(?<=b\\Kc)d",[trim]))), <<"ab:">> = iolist_to_binary(join(re:split("abcd","^abc(?<=b\\Kc)d",[{parts, - 2}]))), - <<"ab:">> = iolist_to_binary(join(re:split("abcd","^abc(?<=b\\Kc)d",[]))), - <<"">> = iolist_to_binary(join(re:split("abcd","^abc(?<!b\\Kq)d",[trim]))), + 2}]))), + <<"ab:">> = iolist_to_binary(join(re:split("abcd","^abc(?<=b\\Kc)d",[]))), + <<"">> = iolist_to_binary(join(re:split("abcd","^abc(?<!b\\Kq)d",[trim]))), <<":">> = iolist_to_binary(join(re:split("abcd","^abc(?<!b\\Kq)d",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abcd","^abc(?<!b\\Kq)d",[]))), - <<":abcd">> = iolist_to_binary(join(re:split("abcd","^((abc|abcx)(*THEN)y|abcd)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abcd","^abc(?<!b\\Kq)d",[]))), + <<":abcd">> = iolist_to_binary(join(re:split("abcd","^((abc|abcx)(*THEN)y|abcd)",[trim]))), <<":abcd::">> = iolist_to_binary(join(re:split("abcd","^((abc|abcx)(*THEN)y|abcd)",[{parts, - 2}]))), - <<":abcd::">> = iolist_to_binary(join(re:split("abcd","^((abc|abcx)(*THEN)y|abcd)",[]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((abc|abcx)(*THEN)y|abcd)",[trim]))), + 2}]))), + <<":abcd::">> = iolist_to_binary(join(re:split("abcd","^((abc|abcx)(*THEN)y|abcd)",[]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((abc|abcx)(*THEN)y|abcd)",[trim]))), <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((abc|abcx)(*THEN)y|abcd)",[{parts, - 2}]))), - <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((abc|abcx)(*THEN)y|abcd)",[]))), - <<"abcxy">> = iolist_to_binary(join(re:split("abcxy","^((abc|abcx)(*THEN)y|abcd)",[trim]))), + 2}]))), + <<"*** Failers">> = iolist_to_binary(join(re:split("*** Failers","^((abc|abcx)(*THEN)y|abcd)",[]))), + <<"abcxy">> = iolist_to_binary(join(re:split("abcxy","^((abc|abcx)(*THEN)y|abcd)",[trim]))), <<"abcxy">> = iolist_to_binary(join(re:split("abcxy","^((abc|abcx)(*THEN)y|abcd)",[{parts, - 2}]))), - <<"abcxy">> = iolist_to_binary(join(re:split("abcxy","^((abc|abcx)(*THEN)y|abcd)",[]))), - <<"yes">> = iolist_to_binary(join(re:split("yes","^((yes|no)(*THEN)(*F))?",[trim]))), + 2}]))), + <<"abcxy">> = iolist_to_binary(join(re:split("abcxy","^((abc|abcx)(*THEN)y|abcd)",[]))), + <<"yes">> = iolist_to_binary(join(re:split("yes","^((yes|no)(*THEN)(*F))?",[trim]))), <<"yes">> = iolist_to_binary(join(re:split("yes","^((yes|no)(*THEN)(*F))?",[{parts, - 2}]))), - <<"yes">> = iolist_to_binary(join(re:split("yes","^((yes|no)(*THEN)(*F))?",[]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|ac)ac|ac",[trim]))), + 2}]))), + <<"yes">> = iolist_to_binary(join(re:split("yes","^((yes|no)(*THEN)(*F))?",[]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|ac)ac|ac",[trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|ac)ac|ac",[{parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|ac)ac|ac",[]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|ac)ac|ac",[]))), <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|(ac)) ac | (a)c",[extended, - trim]))), + trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|(ac)) ac | (a)c",[extended, {parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|(ac)) ac | (a)c",[extended]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*THEN)a)bn|bnn)",[trim]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","(?=a(*COMMIT)b|(ac)) ac | (a)c",[extended]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*THEN)a)bn|bnn)",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*THEN)a)bn|bnn)",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*THEN)a)bn|bnn)",[]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*SKIP)a)bn|bnn",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*THEN)a)bn|bnn)",[]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*SKIP)a)bn|bnn",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*SKIP)a)bn|bnn",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*SKIP)a)bn|bnn",[]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*SKIP)a)bn|bnn)",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*SKIP)a)bn|bnn",[]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*SKIP)a)bn|bnn)",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*SKIP)a)bn|bnn)",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*SKIP)a)bn|bnn)",[]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*PRUNE)a)bn|bnn",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*SKIP)a)bn|bnn)",[]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*PRUNE)a)bn|bnn",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*PRUNE)a)bn|bnn",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*PRUNE)a)bn|bnn",[]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*PRUNE)a)bn|bnn)",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*PRUNE)a)bn|bnn",[]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*PRUNE)a)bn|bnn)",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*PRUNE)a)bn|bnn)",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*PRUNE)a)bn|bnn)",[]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*COMMIT)a)bn|bnn",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*PRUNE)a)bn|bnn)",[]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*COMMIT)a)bn|bnn",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*COMMIT)a)bn|bnn",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*COMMIT)a)bn|bnn",[]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*COMMIT)a)bn|bnn)",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?!b(*COMMIT)a)bn|bnn",[]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*COMMIT)a)bn|bnn)",[trim]))), <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*COMMIT)a)bn|bnn)",[{parts, - 2}]))), - <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*COMMIT)a)bn|bnn)",[]))), - <<"bnn">> = iolist_to_binary(join(re:split("bnn","(?=b(*SKIP)a)bn|bnn",[trim]))), + 2}]))), + <<":n">> = iolist_to_binary(join(re:split("bnn","(?(?!b(*COMMIT)a)bn|bnn)",[]))), + <<"bnn">> = iolist_to_binary(join(re:split("bnn","(?=b(*SKIP)a)bn|bnn",[trim]))), <<"bnn">> = iolist_to_binary(join(re:split("bnn","(?=b(*SKIP)a)bn|bnn",[{parts, - 2}]))), - <<"bnn">> = iolist_to_binary(join(re:split("bnn","(?=b(*SKIP)a)bn|bnn",[]))), - <<"">> = iolist_to_binary(join(re:split("bnn","(?=b(*THEN)a)bn|bnn",[trim]))), + 2}]))), + <<"bnn">> = iolist_to_binary(join(re:split("bnn","(?=b(*SKIP)a)bn|bnn",[]))), + <<"">> = iolist_to_binary(join(re:split("bnn","(?=b(*THEN)a)bn|bnn",[trim]))), <<":">> = iolist_to_binary(join(re:split("bnn","(?=b(*THEN)a)bn|bnn",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("bnn","(?=b(*THEN)a)bn|bnn",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("bnn","(?=b(*THEN)a)bn|bnn",[]))), ok. run54() -> - <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*SKIP)b)..",[trim]))), + <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*SKIP)b)..",[trim]))), <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*SKIP)b)..",[{parts, - 2}]))), - <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*SKIP)b)..",[]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?(?!a(*SKIP)b))",[trim]))), + 2}]))), + <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*SKIP)b)..",[]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?(?!a(*SKIP)b))",[trim]))), <<"ac">> = iolist_to_binary(join(re:split("ac","^(?(?!a(*SKIP)b))",[{parts, - 2}]))), - <<"ac">> = iolist_to_binary(join(re:split("ac","^(?(?!a(*SKIP)b))",[]))), - <<":d">> = iolist_to_binary(join(re:split("acd","^(?!a(*PRUNE)b)..",[trim]))), + 2}]))), + <<"ac">> = iolist_to_binary(join(re:split("ac","^(?(?!a(*SKIP)b))",[]))), + <<":d">> = iolist_to_binary(join(re:split("acd","^(?!a(*PRUNE)b)..",[trim]))), <<":d">> = iolist_to_binary(join(re:split("acd","^(?!a(*PRUNE)b)..",[{parts, - 2}]))), - <<":d">> = iolist_to_binary(join(re:split("acd","^(?!a(*PRUNE)b)..",[]))), - <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*PRUNE)b)..",[trim]))), + 2}]))), + <<":d">> = iolist_to_binary(join(re:split("acd","^(?!a(*PRUNE)b)..",[]))), + <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*PRUNE)b)..",[trim]))), <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*PRUNE)b)..",[{parts, - 2}]))), - <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*PRUNE)b)..",[]))), - <<"">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)",[trim]))), + 2}]))), + <<":d">> = iolist_to_binary(join(re:split("acd","(?!a(*PRUNE)b)..",[]))), + <<"">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)",[trim]))), <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)",[]))), - <<":CD">> = iolist_to_binary(join(re:split("CD","^(A(*THEN)B|C(*THEN)D)",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ba","\\A.*?(?:a|bc)",[]))), + <<":CD">> = iolist_to_binary(join(re:split("CD","^(A(*THEN)B|C(*THEN)D)",[trim]))), <<":CD:">> = iolist_to_binary(join(re:split("CD","^(A(*THEN)B|C(*THEN)D)",[{parts, - 2}]))), - <<":CD:">> = iolist_to_binary(join(re:split("CD","^(A(*THEN)B|C(*THEN)D)",[]))), - <<"">> = iolist_to_binary(join(re:split("1234","^\\d*\\w{4}",[trim]))), + 2}]))), + <<":CD:">> = iolist_to_binary(join(re:split("CD","^(A(*THEN)B|C(*THEN)D)",[]))), + <<"">> = iolist_to_binary(join(re:split("1234","^\\d*\\w{4}",[trim]))), <<":">> = iolist_to_binary(join(re:split("1234","^\\d*\\w{4}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("1234","^\\d*\\w{4}",[]))), - <<"123">> = iolist_to_binary(join(re:split("123","^\\d*\\w{4}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("1234","^\\d*\\w{4}",[]))), + <<"123">> = iolist_to_binary(join(re:split("123","^\\d*\\w{4}",[trim]))), <<"123">> = iolist_to_binary(join(re:split("123","^\\d*\\w{4}",[{parts, - 2}]))), - <<"123">> = iolist_to_binary(join(re:split("123","^\\d*\\w{4}",[]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[trim]))), + 2}]))), + <<"123">> = iolist_to_binary(join(re:split("123","^\\d*\\w{4}",[]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[]))), <<"">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^[^b]*\\w{4}",[caseless]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[caseless, - trim]))), + trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[caseless, {parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[caseless]))), - <<"">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^[^b]*\\w{4}",[caseless]))), + <<"">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[{parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[]))), <<"">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[caseless, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[caseless, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[caseless]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaa","^a*\\w{4}",[caseless]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[caseless, - trim]))), + trim]))), <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[caseless, {parts, - 2}]))), - <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[caseless]))), - <<":1:non-sp1:non-sp2">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), + 2}]))), + <<"aaa">> = iolist_to_binary(join(re:split("aaa","^a*\\w{4}",[caseless]))), + <<":1:non-sp1:non-sp2">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[trim]))), <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[{parts, - 2}]))), - <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), - <<"AZ">> = iolist_to_binary(join(re:split("AZ","^A\\xZ",[trim]))), + 2}]))), + <<":1:non-sp1:non-sp2:">> = iolist_to_binary(join(re:split("1 IN SOA non-sp1 non-sp2(","^(\\d+)\\s+IN\\s+SOA\\s+(\\S+)\\s+(\\S+)\\s*\\(\\s*$",[]))), + <<"AZ">> = iolist_to_binary(join(re:split("AZ","^A\\xZ",[trim]))), <<"AZ">> = iolist_to_binary(join(re:split("AZ","^A\\xZ",[{parts, - 2}]))), - <<"AZ">> = iolist_to_binary(join(re:split("AZ","^A\\xZ",[]))), - <<"">> = iolist_to_binary(join(re:split("ASB","^A\\o{123}B",[trim]))), + 2}]))), + <<"AZ">> = iolist_to_binary(join(re:split("AZ","^A\\xZ",[]))), + <<"">> = iolist_to_binary(join(re:split("ASB","^A\\o{123}B",[trim]))), <<":">> = iolist_to_binary(join(re:split("ASB","^A\\o{123}B",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("ASB","^A\\o{123}B",[]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("ASB","^A\\o{123}B",[]))), <<"">> = iolist_to_binary(join(re:split("aaaab"," ^ a + + b $ ",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + + b $ ",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + + b $ ",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + + b $ ",[extended]))), <<"">> = iolist_to_binary(join(re:split("aaaab"," ^ a + #comment - + b $ ",[extended,trim]))), + + b $ ",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + #comment - + b $ ",[extended,{parts,2}]))), + + b $ ",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + #comment - + b $ ",[extended]))), + + b $ ",[extended]))), <<"">> = iolist_to_binary(join(re:split("aaaab"," ^ a + #comment #comment - + b $ ",[extended,trim]))), + + b $ ",[extended,trim]))), <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + #comment #comment - + b $ ",[extended,{parts,2}]))), + + b $ ",[extended,{parts,2}]))), <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ a + #comment #comment - + b $ ",[extended]))), + + b $ ",[extended]))), ok. run55() -> <<"">> = iolist_to_binary(join(re:split("aaaab"," ^ (?> a + ) b $ ",[extended, - trim]))), + trim]))), <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ (?> a + ) b $ ",[extended, {parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ (?> a + ) b $ ",[extended]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aaaab"," ^ (?> a + ) b $ ",[extended]))), <<":aaaa">> = iolist_to_binary(join(re:split("aaaab"," ^ ( a + ) + + \\w $ ",[extended, - trim]))), + trim]))), <<":aaaa:">> = iolist_to_binary(join(re:split("aaaab"," ^ ( a + ) + + \\w $ ",[extended, {parts, - 2}]))), - <<":aaaa:">> = iolist_to_binary(join(re:split("aaaab"," ^ ( a + ) + + \\w $ ",[extended]))), - <<"acb">> = iolist_to_binary(join(re:split("acb","(?:x|(?:(xx|yy)+|x|x|x|x|x)|a|a|a)bc",[trim]))), + 2}]))), + <<":aaaa:">> = iolist_to_binary(join(re:split("aaaab"," ^ ( a + ) + + \\w $ ",[extended]))), + <<"acb">> = iolist_to_binary(join(re:split("acb","(?:x|(?:(xx|yy)+|x|x|x|x|x)|a|a|a)bc",[trim]))), <<"acb">> = iolist_to_binary(join(re:split("acb","(?:x|(?:(xx|yy)+|x|x|x|x|x)|a|a|a)bc",[{parts, - 2}]))), - <<"acb">> = iolist_to_binary(join(re:split("acb","(?:x|(?:(xx|yy)+|x|x|x|x|x)|a|a|a)bc",[]))), - <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]*+|\\\"\\\")*+\\\")++",[trim]))), + 2}]))), + <<"acb">> = iolist_to_binary(join(re:split("acb","(?:x|(?:(xx|yy)+|x|x|x|x|x)|a|a|a)bc",[]))), + <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]*+|\\\"\\\")*+\\\")++",[trim]))), <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]*+|\\\"\\\")*+\\\")++",[{parts, - 2}]))), - <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]*+|\\\"\\\")*+\\\")++",[]))), - <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")*+\\\")++",[trim]))), + 2}]))), + <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]*+|\\\"\\\")*+\\\")++",[]))), + <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")*+\\\")++",[trim]))), <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")*+\\\")++",[{parts, - 2}]))), - <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")*+\\\")++",[]))), - <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")++\\\")++",[trim]))), + 2}]))), + <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")*+\\\")++",[]))), + <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")++\\\")++",[trim]))), <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")++\\\")++",[{parts, - 2}]))), - <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")++\\\")++",[]))), - <<": AFTER ::\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A([^\\\"1]++|[\\\"2]([^\\\"3]*+|[\\\"4][\\\"5])*+[\\\"6])++",[trim]))), + 2}]))), + <<":\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A(?:[^\\\"]++|\\\"(?:[^\\\"]++|\\\"\\\")++\\\")++",[]))), + <<": AFTER ::\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A([^\\\"1]++|[\\\"2]([^\\\"3]*+|[\\\"4][\\\"5])*+[\\\"6])++",[trim]))), <<": AFTER ::\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A([^\\\"1]++|[\\\"2]([^\\\"3]*+|[\\\"4][\\\"5])*+[\\\"6])++",[{parts, - 2}]))), - <<": AFTER ::\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A([^\\\"1]++|[\\\"2]([^\\\"3]*+|[\\\"4][\\\"5])*+[\\\"6])++",[]))), - <<":t test">> = iolist_to_binary(join(re:split("test test","^\\w+(?>\\s*)(?<=\\w)",[trim]))), + 2}]))), + <<": AFTER ::\"NOT MATCHED">> = iolist_to_binary(join(re:split("NON QUOTED \"QUOT\"\"ED\" AFTER \"NOT MATCHED","\\A([^\\\"1]++|[\\\"2]([^\\\"3]*+|[\\\"4][\\\"5])*+[\\\"6])++",[]))), + <<":t test">> = iolist_to_binary(join(re:split("test test","^\\w+(?>\\s*)(?<=\\w)",[trim]))), <<":t test">> = iolist_to_binary(join(re:split("test test","^\\w+(?>\\s*)(?<=\\w)",[{parts, - 2}]))), - <<":t test">> = iolist_to_binary(join(re:split("test test","^\\w+(?>\\s*)(?<=\\w)",[]))), - <<":a">> = iolist_to_binary(join(re:split("acl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), + 2}]))), + <<":t test">> = iolist_to_binary(join(re:split("test test","^\\w+(?>\\s*)(?<=\\w)",[]))), + <<":a">> = iolist_to_binary(join(re:split("acl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), <<":a::">> = iolist_to_binary(join(re:split("acl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[{parts, - 2}]))), - <<":a::">> = iolist_to_binary(join(re:split("acl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), - <<"::b">> = iolist_to_binary(join(re:split("bdl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), + 2}]))), + <<":a::">> = iolist_to_binary(join(re:split("acl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), + <<"::b">> = iolist_to_binary(join(re:split("bdl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), <<"::b:">> = iolist_to_binary(join(re:split("bdl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[{parts, - 2}]))), - <<"::b:">> = iolist_to_binary(join(re:split("bdl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), - <<"a">> = iolist_to_binary(join(re:split("adl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), + 2}]))), + <<"::b:">> = iolist_to_binary(join(re:split("bdl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), + <<"a">> = iolist_to_binary(join(re:split("adl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), <<"a:::">> = iolist_to_binary(join(re:split("adl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[{parts, - 2}]))), - <<"a:::">> = iolist_to_binary(join(re:split("adl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), - <<"bc">> = iolist_to_binary(join(re:split("bcl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), + 2}]))), + <<"a:::">> = iolist_to_binary(join(re:split("adl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), + <<"bc">> = iolist_to_binary(join(re:split("bcl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[trim]))), <<"bc:::">> = iolist_to_binary(join(re:split("bcl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[{parts, - 2}]))), - <<"bc:::">> = iolist_to_binary(join(re:split("bcl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), - <<"">> = iolist_to_binary(join(re:split("abc","\\sabc",[trim]))), + 2}]))), + <<"bc:::">> = iolist_to_binary(join(re:split("bcl","(?P<Name>a)?(?P<Name2>b)?(?(<Name>)c|d)*l",[]))), + <<"">> = iolist_to_binary(join(re:split("abc","\\sabc",[trim]))), <<":">> = iolist_to_binary(join(re:split("abc","\\sabc",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("abc","\\sabc",[]))), - <<"">> = iolist_to_binary(join(re:split("aa]]","[\\Qa]\\E]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("abc","\\sabc",[]))), + <<"">> = iolist_to_binary(join(re:split("aa]]","[\\Qa]\\E]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("aa]]","[\\Qa]\\E]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aa]]","[\\Qa]\\E]+",[]))), - <<"">> = iolist_to_binary(join(re:split("aa]]","[\\Q]a\\E]+",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aa]]","[\\Qa]\\E]+",[]))), + <<"">> = iolist_to_binary(join(re:split("aa]]","[\\Q]a\\E]+",[trim]))), <<":">> = iolist_to_binary(join(re:split("aa]]","[\\Q]a\\E]+",[{parts, - 2}]))), - <<":">> = iolist_to_binary(join(re:split("aa]]","[\\Q]a\\E]+",[]))), - <<"1::::::2::::::3::::::4:abcd:abcd">> = iolist_to_binary(join(re:split("1234abcd","(?:((abcd))|(((?:(?:(?:(?:abc|(?:abcdef))))b)abcdefghi)abc)|((*ACCEPT)))",[trim]))), + 2}]))), + <<":">> = iolist_to_binary(join(re:split("aa]]","[\\Q]a\\E]+",[]))), + <<"1::::::2::::::3::::::4:abcd:abcd">> = iolist_to_binary(join(re:split("1234abcd","(?:((abcd))|(((?:(?:(?:(?:abc|(?:abcdef))))b)abcdefghi)abc)|((*ACCEPT)))",[trim]))), <<"1::::::234abcd">> = iolist_to_binary(join(re:split("1234abcd","(?:((abcd))|(((?:(?:(?:(?:abc|(?:abcdef))))b)abcdefghi)abc)|((*ACCEPT)))",[{parts, - 2}]))), - <<"1::::::2::::::3::::::4:abcd:abcd::::">> = iolist_to_binary(join(re:split("1234abcd","(?:((abcd))|(((?:(?:(?:(?:abc|(?:abcdef))))b)abcdefghi)abc)|((*ACCEPT)))",[]))), + 2}]))), + <<"1::::::2::::::3::::::4:abcd:abcd::::">> = iolist_to_binary(join(re:split("1234abcd","(?:((abcd))|(((?:(?:(?:(?:abc|(?:abcdef))))b)abcdefghi)abc)|((*ACCEPT)))",[]))), ok. run56() -> - <<"b:a:c">> = iolist_to_binary(join(re:split("baaaaaaaaac","(?1)(?#?'){8}(a)",[trim]))), + <<"b:a:c">> = iolist_to_binary(join(re:split("baaaaaaaaac","(?1)(?#?'){8}(a)",[trim]))), <<"b:a:c">> = iolist_to_binary(join(re:split("baaaaaaaaac","(?1)(?#?'){8}(a)",[{parts, - 2}]))), - <<"b:a:c">> = iolist_to_binary(join(re:split("baaaaaaaaac","(?1)(?#?'){8}(a)",[]))), - <<"a::b::c::d">> = iolist_to_binary(join(re:split("abcd","(?|(\\k'Pm')|(?'Pm'))",[trim]))), + 2}]))), + <<"b:a:c">> = iolist_to_binary(join(re:split("baaaaaaaaac","(?1)(?#?'){8}(a)",[]))), + <<"a::b::c::d">> = iolist_to_binary(join(re:split("abcd","(?|(\\k'Pm')|(?'Pm'))",[trim]))), <<"a::bcd">> = iolist_to_binary(join(re:split("abcd","(?|(\\k'Pm')|(?'Pm'))",[{parts, - 2}]))), - <<"a::b::c::d::">> = iolist_to_binary(join(re:split("abcd","(?|(\\k'Pm')|(?'Pm'))",[]))), - <<" :Fred:099">> = iolist_to_binary(join(re:split(" Fred:099","(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\\s])",[trim]))), + 2}]))), + <<"a::b::c::d::">> = iolist_to_binary(join(re:split("abcd","(?|(\\k'Pm')|(?'Pm'))",[]))), + <<" :Fred:099">> = iolist_to_binary(join(re:split(" Fred:099","(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\\s])",[trim]))), <<" :Fred:099">> = iolist_to_binary(join(re:split(" Fred:099","(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\\s])",[{parts, - 2}]))), - <<" :Fred:099">> = iolist_to_binary(join(re:split(" Fred:099","(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\\s])",[]))), - <<" ">> = iolist_to_binary(join(re:split(" X","(?=.*X)X$",[trim]))), + 2}]))), + <<" :Fred:099">> = iolist_to_binary(join(re:split(" Fred:099","(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[,;:])(?=.{8,16})(?!.*[\\s])",[]))), + <<" ">> = iolist_to_binary(join(re:split(" X","(?=.*X)X$",[trim]))), <<" :">> = iolist_to_binary(join(re:split(" X","(?=.*X)X$",[{parts, - 2}]))), - <<" :">> = iolist_to_binary(join(re:split(" X","(?=.*X)X$",[]))), + 2}]))), + <<" :">> = iolist_to_binary(join(re:split(" X","(?=.*X)X$",[]))), + <<">:::<">> = iolist_to_binary(join(re:split(">XXX<","X+(?#comment)?",[trim]))), + <<">:XX<">> = iolist_to_binary(join(re:split(">XXX<","X+(?#comment)?",[{parts, + 2}]))), + <<">:::<">> = iolist_to_binary(join(re:split(">XXX<","X+(?#comment)?",[]))), + <<":pokus">> = iolist_to_binary(join(re:split("pokus."," (?<word> \\w+ )* \\. ",[extended, + caseless, + trim]))), + <<":pokus:">> = iolist_to_binary(join(re:split("pokus."," (?<word> \\w+ )* \\. ",[extended, + caseless, + {parts, + 2}]))), + <<":pokus:">> = iolist_to_binary(join(re:split("pokus."," (?<word> \\w+ )* \\. ",[extended, + caseless]))), + <<"">> = iolist_to_binary(join(re:split("pokus.","(?(DEFINE) (?<word> \\w+ ) ) (?&word)* \\.",[extended, + caseless, + trim]))), + <<"::">> = iolist_to_binary(join(re:split("pokus.","(?(DEFINE) (?<word> \\w+ ) ) (?&word)* \\.",[extended, + caseless, + {parts, + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("pokus.","(?(DEFINE) (?<word> \\w+ ) ) (?&word)* \\.",[extended, + caseless]))), + <<"::pokus">> = iolist_to_binary(join(re:split("pokus.","(?(DEFINE) (?<word> \\w+ ) ) ( (?&word)* ) \\.",[extended, + caseless, + trim]))), + <<"::pokus:">> = iolist_to_binary(join(re:split("pokus.","(?(DEFINE) (?<word> \\w+ ) ) ( (?&word)* ) \\.",[extended, + caseless, + {parts, + 2}]))), + <<"::pokus:">> = iolist_to_binary(join(re:split("pokus.","(?(DEFINE) (?<word> \\w+ ) ) ( (?&word)* ) \\.",[extended, + caseless]))), + <<"">> = iolist_to_binary(join(re:split("pokus.","(?&word)* (?(DEFINE) (?<word> \\w+ ) ) \\.",[extended, + caseless, + trim]))), + <<"::">> = iolist_to_binary(join(re:split("pokus.","(?&word)* (?(DEFINE) (?<word> \\w+ ) ) \\.",[extended, + caseless, + {parts, + 2}]))), + <<"::">> = iolist_to_binary(join(re:split("pokus.","(?&word)* (?(DEFINE) (?<word> \\w+ ) ) \\.",[extended, + caseless]))), + <<":hokus">> = iolist_to_binary(join(re:split("pokus.hokus","(?&word)* \\. (?<word> \\w+ )",[extended, + caseless, + trim]))), + <<":hokus:">> = iolist_to_binary(join(re:split("pokus.hokus","(?&word)* \\. (?<word> \\w+ )",[extended, + caseless, + {parts, + 2}]))), + <<":hokus:">> = iolist_to_binary(join(re:split("pokus.hokus","(?&word)* \\. (?<word> \\w+ )",[extended, + caseless]))), ok. diff --git a/lib/tools/doc/src/notes.xml b/lib/tools/doc/src/notes.xml index fd41e2cbeb..09ae5ef04a 100644 --- a/lib/tools/doc/src/notes.xml +++ b/lib/tools/doc/src/notes.xml @@ -74,6 +74,21 @@ </section> +<section><title>Tools 3.1.0.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p><c>cover</c> would fail to start if two processes + tried to start it at the exact same time.</p> + <p> + Own Id: OTP-15813 Aux Id: ERL-943 </p> + </item> + </list> + </section> + +</section> + <section><title>Tools 3.1</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -171,6 +186,21 @@ </section> +<section><title>Tools 2.11.2.2</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p><c>cover</c> would fail to start if two processes + tried to start it at the exact same time.</p> + <p> + Own Id: OTP-15813 Aux Id: ERL-943 </p> + </item> + </list> + </section> + +</section> + <section><title>Tools 2.11.2.1</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/tools/src/cover.erl b/lib/tools/src/cover.erl index 8fe866cb69..2b3af417b6 100644 --- a/lib/tools/src/cover.erl +++ b/lib/tools/src/cover.erl @@ -196,6 +196,8 @@ start() -> receive {?SERVER,started} -> {ok,Pid}; + {?SERVER,{error,Error}} -> + {error,Error}; {'DOWN', Ref, _Type, _Object, Info} -> {error,Info} end, @@ -645,19 +647,31 @@ remote_reply(MainNode,Reply) -> %%%---------------------------------------------------------------------- init_main(Starter) -> - register(?SERVER,self()), - ?COVER_MAPPING_TABLE = ets:new(?COVER_MAPPING_TABLE, - [ordered_set, public, named_table]), - ?COVER_CLAUSE_TABLE = ets:new(?COVER_CLAUSE_TABLE, [set, public, - named_table]), - ?BINARY_TABLE = ets:new(?BINARY_TABLE, [set, public, named_table]), - ?COLLECTION_TABLE = ets:new(?COLLECTION_TABLE, [set, public, - named_table]), - ?COLLECTION_CLAUSE_TABLE = ets:new(?COLLECTION_CLAUSE_TABLE, [set, public, - named_table]), - ok = net_kernel:monitor_nodes(true), - Starter ! {?SERVER,started}, - main_process_loop(#main_state{}). + try register(?SERVER,self()) of + true -> + ?COVER_MAPPING_TABLE = ets:new(?COVER_MAPPING_TABLE, + [ordered_set, public, named_table]), + ?COVER_CLAUSE_TABLE = ets:new(?COVER_CLAUSE_TABLE, [set, public, + named_table]), + ?BINARY_TABLE = ets:new(?BINARY_TABLE, [set, public, named_table]), + ?COLLECTION_TABLE = ets:new(?COLLECTION_TABLE, [set, public, + named_table]), + ?COLLECTION_CLAUSE_TABLE = ets:new(?COLLECTION_CLAUSE_TABLE, + [set, public, named_table]), + ok = net_kernel:monitor_nodes(true), + Starter ! {?SERVER,started}, + main_process_loop(#main_state{}) + catch + error:badarg -> + %% The server's already registered; either report that it's already + %% started or try again if it died before we could find its pid. + case whereis(?SERVER) of + undefined -> + init_main(Starter); + Pid -> + Starter ! {?SERVER, {error, {already_started, Pid}}} + end + end. main_process_loop(State) -> receive diff --git a/lib/tools/test/cover_SUITE.erl b/lib/tools/test/cover_SUITE.erl index ee58fd7a10..462767f430 100644 --- a/lib/tools/test/cover_SUITE.erl +++ b/lib/tools/test/cover_SUITE.erl @@ -37,7 +37,7 @@ all() -> dont_reconnect_after_stop, stop_node_after_disconnect, export_import, otp_5031, otp_6115, otp_8270, otp_10979_hanging_node, otp_14817, - local_only], + local_only, startup_race], case whereis(cover_server) of undefined -> [coverage,StartStop ++ NoStartStop]; @@ -1775,7 +1775,32 @@ local_only(Config) -> {ok,Name} = test_server:start_node(?FUNCTION_NAME, slave, []), {error,local_only} = cover:start([Name]), test_server:stop_node(Name), + ok. +%% ERL-943; We should not crash on startup when multiple servers race to +%% register the server name. +startup_race(Config) when is_list(Config) -> + PidRefs = [spawn_monitor(fun() -> + case cover:start() of + {error, {already_started, _Pid}} -> + ok; + {ok, _Pid} -> + ok + end + end) || _<- lists:seq(1,8)], + startup_race_1(PidRefs). + +startup_race_1([{Pid, Ref} | PidRefs]) -> + receive + {'DOWN', Ref, process, Pid, normal} -> + startup_race_1(PidRefs); + {'DOWN', Ref, process, Pid, _Other} -> + ct:fail("Cover server crashed on startup.") + after 5000 -> + ct:fail("Timed out.") + end; +startup_race_1([]) -> + cover:stop(), ok. %%--Auxiliary------------------------------------------------------------ diff --git a/lib/wx/c_src/Makefile.in b/lib/wx/c_src/Makefile.in index 8ec64bea7e..daadf5e785 100644 --- a/lib/wx/c_src/Makefile.in +++ b/lib/wx/c_src/Makefile.in @@ -70,7 +70,7 @@ else RC_FILE = endif -WX_OBJECTS = $(GENERAL_O) $(GENERATED_O) $(RC_FILE) +WX_OBJECTS = $(GENERATED_O) $(GENERAL_O) $(RC_FILE) OBJECTS = $(WX_OBJECTS) $(GL_OBJECTS) @@ -86,7 +86,7 @@ LD = $(CXX) LDFLAGS = @LDFLAGS@ RESCOMP = @WX_RESCOMP@ -ifeq (@WX_HAVE_STATIC_LIBS@,true) +ifeq (@WX_HAVE_STATIC_LIBS@,true) OPT_WX_LIBS = @WX_LIBS_STATIC@ DEBUG_WX_LIBS = @DEBUG_WX_LIBS_STATIC@ else @@ -97,14 +97,16 @@ endif ifeq ($(TYPE),debug) WX_CFLAGS = @DEBUG_WX_CFLAGS@ CFLAGS = @DEBUG_CFLAGS@ -WX_CXX_FLAGS = @DEBUG_WX_CXXFLAGS@ +WX_CXX_FLAGS = @DEBUG_WX_CXXFLAGS@ CXX_FLAGS = @DEBUG_CXXFLAGS@ +CXX_NO_OPT_FLAGS = @DEBUG_CXXFLAGS@ WX_LIBS = $(DEBUG_WX_LIBS) else WX_CFLAGS = @WX_CFLAGS@ CFLAGS = @CFLAGS@ -WX_CXX_FLAGS = @WX_CXXFLAGS@ +WX_CXX_FLAGS = @WX_CXXFLAGS@ CXX_FLAGS = @CXXFLAGS@ +CXX_NO_OPT_FLAGS = @CXXNOOPTFLAGS@ WX_LIBS = $(OPT_WX_LIBS) endif @@ -113,6 +115,7 @@ GL_LIBS = @GL_LIBS@ CC_O = $(V_CC) -c $(CFLAGS) $(WX_CFLAGS) $(COMMON_CFLAGS) OBJC_CC_O = $(OBJC_CC) -c $(CFLAGS) $(OBJC_CFLAGS) $(WX_CFLAGS) $(COMMON_CFLAGS) CXX_O = $(V_CXX) -c $(CXX_FLAGS) $(WX_CXX_FLAGS) $(COMMON_CFLAGS) +CXX_O_NO_OPT = $(V_CXX) -c $(CXX_NO_OPT_FLAGS) $(WX_CXX_FLAGS) $(COMMON_CFLAGS) # Targets @@ -152,6 +155,10 @@ $(SYS_TYPE)/wxe_ps_init.o: wxe_ps_init.c $(V_at)mkdir -p $(SYS_TYPE) $(cc_verbose)$(OBJC_CC_O) $< -o $@ +$(SYS_TYPE)/wxe_funcs.o: gen/wxe_funcs.cpp + $(V_at)mkdir -p $(SYS_TYPE) + $(CXX_O_NO_OPT) $< -o $@ + $(SYS_TYPE)/%.o: gen/%.cpp $(V_at)mkdir -p $(SYS_TYPE) $(CXX_O) $< -o $@ diff --git a/lib/wx/c_src/wxe_driver.c b/lib/wx/c_src/wxe_driver.c index c9d299e0df..b94ec2f32d 100644 --- a/lib/wx/c_src/wxe_driver.c +++ b/lib/wx/c_src/wxe_driver.c @@ -214,7 +214,7 @@ standard_outputv(ErlDrvData drv_data, ErlIOVec* ev) if(binref == NULL) { /* realloc */ max = sd->max_bins + DEF_BINS; - driver_realloc(sd->bin, sizeof(WXEBinRef)*max); + sd->bin = driver_realloc(sd->bin, sizeof(WXEBinRef)*max); for(i=sd->max_bins; i < max; i++) { sd->bin[i].from = 0; } diff --git a/lib/wx/configure.in b/lib/wx/configure.in index dbe237cd74..f35e6cdbd0 100644 --- a/lib/wx/configure.in +++ b/lib/wx/configure.in @@ -30,17 +30,11 @@ if test -f ./CONF_INFO; then fi if test -z "$ERL_TOP" || test ! -d $ERL_TOP ; then - AC_CONFIG_AUX_DIRS(autoconf) - WX_BUILDING_INSIDE_ERLSRC=false + AC_MSG_ERROR([ERL_TOP is not set]) else erl_top=${ERL_TOP} - if test -d $erl_top/erts/autoconf; then - AC_CONFIG_AUX_DIRS($erl_top/erts/autoconf) - WX_BUILDING_INSIDE_ERLSRC=true - else - AC_CONFIG_AUX_DIRS(autoconf) - WX_BUILDING_INSIDE_ERLSRC=false - fi + AC_CONFIG_AUX_DIRS($erl_top/erts/autoconf) + WX_BUILDING_INSIDE_ERLSRC=true fi if test "X$host" != "Xfree_source" -a "X$host" != "Xwin32"; then @@ -67,6 +61,20 @@ AC_PROG_CXX AC_PROG_RANLIB AC_PROG_CPP +AC_LANG_PUSH([C++]) +AC_COMPILE_IFELSE([AC_LANG_PROGRAM( + [[#ifndef __cplusplus + #error "broken C++" + #endif]])],, + [CXX=;]) +AC_LANG_POP([C++]) +if test "X$CXX" = X ; then + echo "Can not find C++ compiler" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false + AC_MSG_WARN([Can not find C++ compiler]) +fi +WXERL_CAN_BUILD_DRIVER=false + AC_MSG_NOTICE(Building for [$host_os]) WXERL_CAN_BUILD_DRIVER=true @@ -139,13 +147,9 @@ case $host_os in if test X$APPLE_CC = X -o X$APPLE_CXX = X; then AC_MSG_RESULT([no]) dnl Complete failure, we cannot build Cocoa code - if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then - AC_MSG_ERROR([Can not find compiler to compile Cocoa applications]) - else - echo "Can not find compiler to compile Cocoa applications" > ./CONF_INFO - WXERL_CAN_BUILD_DRIVER=false - AC_MSG_WARN([Can not find compiler to compile Cocoa applications]) - fi + echo "Can not find compiler to compile Cocoa applications" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false + AC_MSG_WARN([Can not find compiler to compile Cocoa applications]) WXERL_CAN_BUILD_DRIVER=false else dnl We think we found an Apple compiler and will add @@ -225,11 +229,21 @@ case $host_os in ;; *) DEBUG_CFLAGS="-g -Wall -fPIC $CFLAGS -DDEBUG" - CFLAGS="-g -Wall -O2 -fPIC $CFLAGS -fomit-frame-pointer -fno-strict-aliasing" + CFLAGS="-Wall -fPIC $CFLAGS -fomit-frame-pointer -fno-strict-aliasing" ;; esac -dnl +dnl +dnl Use -O1 -fno-move-loop-invariants for wxe_funcs.cpp to reduce +dnl compilation time +dnl + +if test "x$GCC" = xyes -a X"$host_os" != X"win32" ; then + CXXNOOPT="-O1" + LM_TRY_ENABLE_CFLAG([-fno-move-loop-invariants], [CXXNOOPT]) +fi + +dnl dnl Opengl tests dnl @@ -251,11 +265,13 @@ if test X"$host_os" != X"win32" ; then AC_CHECK_HEADERS([GL/gl.h]) if test X"$ac_cv_header_GL_gl_h" != Xyes ; then AC_MSG_WARN([No OpenGL headers found, wx will NOT be usable]) + echo "No OpenGL headers found, wx will NOT be usable" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false CPPFLAGS="$saved_CPPFLAGS" - else + else GL_LIBS="-L/usr/local/lib $GL_LIBS" fi - else + else GL_LIBS="-L/usr/X11R6/lib $GL_LIBS" fi fi @@ -270,6 +286,8 @@ if test X"$host_os" != X"win32" ; then test X"$ac_cv_header_OpenGL_glu_h" != Xyes then AC_MSG_WARN([No GLU headers found, wx will NOT be usable]) + echo "No GLU headers (glu.h) found, wx will NOT be usable" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false fi else AC_CHECK_HEADERS([gl/glu.h],[],[],[#include <windows.h>]) @@ -280,47 +298,17 @@ AC_SUBST(GL_LIBS) DEBUG_CXXFLAGS="$CXXFLAGS $DEBUG_CFLAGS $CPPFLAGS" DEBUG_CFLAGS="$DEBUG_CFLAGS $CPPFLAGS $C_ONLY_FLAGS" -CXXFLAGS="$CXXFLAGS $CFLAGS $CPPFLAGS" +CXXNOOPTFLAGS="$CXXFLAGS $CFLAGS $CPPFLAGS $CXXNOOPT" +CXXFLAGS="$CXXFLAGS $CFLAGS $CPPFLAGS" CFLAGS="$CFLAGS $CPPFLAGS $C_ONLY_FLAGS" AC_SUBST(DEBUG_CFLAGS) AC_SUBST(DEBUG_CXXFLAGS) - -if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then - AC_MSG_CHECKING(for erl) - if test X$ERL != X; then - AC_MSG_RESULT([yes; using $ERL]) - else - type erl >/dev/null 2>&1 - if test $? -eq 0 ; then - ERL=erl - AC_MSG_RESULT([yes]) - else - AC_MSG_ERROR([Cannot find erl in path]) - fi - fi - AC_MSG_CHECKING(for erlc) - if test X$ERLC != X; then - AC_MSG_RESULT([yes; using $ERLC]) - else - type erlc >/dev/null 2>&1 - if test $? -eq 0 ; then - ERLC=erlc - AC_MSG_RESULT([yes]) - else - AC_MSG_ERROR([Cannot find erlc in path]) - fi - fi - ERLANG_ROOT_DIR=`erl -noshell -eval 'io:format("~s~n",[[code:root_dir()]])' -s erlang halt` - AC_MSG_NOTICE(ERL ROOT DIR: [$ERLANG_ROOT_DIR]) - ERLWX_VSN=`grep WX_VSN $srcdir/vsn.mk | sed 's/^.*=[ ]*//'` -else - ERLC=erlc - ERL=erl - ERLANG_ROOT_DIR=$ERL_TOP - AC_SUBST(ERLC) -fi +ERLC=erlc +ERL=erl +ERLANG_ROOT_DIR=$ERL_TOP +AC_SUBST(ERLC) AC_SUBST(WX_BUILDING_INSIDE_ERLSRC) AC_SUBST(ERLANG_ROOT_DIR) @@ -329,7 +317,7 @@ dnl dnl Check for wxwidgets dnl if test "$cross_compiling" = "yes"; then - echo "Cross compilation of the wx driver is not supported yet, wx will NOT be usable" > ./CONF_INFO + echo "Cross compilation of the wx driver is not supported yet, wx will NOT be usable" >> ./CONF_INFO WXERL_CAN_BUILD_DRIVER=false elif test X"$MIXED_CYGWIN_VC" = X"no" -a X"$MIXED_MSYS_VC" = X"no"; then WX_VERSION=`wx-config --version` @@ -398,13 +386,9 @@ define(wx_warn_text,[ wxWidgets version is $reqwx or above.]) if test "$wxWin" != 1; then - if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then - AC_MSG_ERROR([wx_warn_text]) - else - echo "wxWidgets not found, wx will NOT be usable" > ./CONF_INFO - WXERL_CAN_BUILD_DRIVER=false - AC_MSG_WARN([wx_warn_text]) - fi + echo "wxWidgets not found, wx will NOT be usable" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false + AC_MSG_WARN([wx_warn_text]) fi else AC_MSG_CHECKING(for wxWidgets in standard locations) @@ -502,13 +486,9 @@ else if test -z "$WX_LIBS_STATIC"; then AC_MSG_RESULT([failed]) - if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then - AC_MSG_ERROR([Cannot find core lib version for wxWidgets]) - else - echo "No usable wxWidgets not found, wx will not be useable" > ./CONF_INFO - WXERL_CAN_BUILD_DRIVER=false - AC_MSG_WARN([Cannot find core lib version for wxWidgets]) - fi + echo "No usable wxWidgets not found, wx will not be useable" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false + AC_MSG_WARN([Cannot find core lib version for wxWidgets]) fi WX_HAVE_STATIC_LIBS=true AC_SUBST(WX_CFLAGS) @@ -550,8 +530,8 @@ AC_MSG_RESULT($HAVE_GL_SUPPORT) AC_SUBST(HAVE_GL_SUPPORT) if test X"$HAVE_GL_SUPPORT" != X"yes" ; then - echo "wxWidgets don't have gl support, wx will NOT be useable" > ./CONF_INFO - WXERL_CAN_BUILD_DRIVER=false + echo "wxWidgets don't have gl support, wx will NOT be useable" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false fi dnl Check for GLintptr @@ -610,7 +590,7 @@ dnl AC_CHECK_HEADERS([wx/stc/stc.h], [], [WXERL_CAN_BUILD_DRIVER=false - echo "wxWidgets don't have wxStyledTextControl (stc.h), wx will NOT be useable" > ./CONF_INFO + echo "wxWidgets don't have wxStyledTextControl (stc.h), wx will NOT be useable" >> ./CONF_INFO AC_MSG_WARN([Can not find wx/stc/stc.h $CXXFLAGS]) ], [#ifdef WIN32 @@ -670,9 +650,9 @@ AC_LANG_POP(C++) AC_MSG_RESULT($CAN_LINK_WX) if test X"$CAN_LINK_WX" != X"yes" ; then - echo "Can not link the wx driver, wx will NOT be useable" > ./CONF_INFO - WXERL_CAN_BUILD_DRIVER=false - AC_MSG_WARN([Can not link wx program are all developer packages installed?]) + echo "Can not link the wx driver, wx will NOT be useable" >> ./CONF_INFO + WXERL_CAN_BUILD_DRIVER=false + AC_MSG_WARN([Can not link wx program are all developer packages installed?]) fi fi dnl - if test "$WXERL_CAN_BUILD_DRIVER" != "false" @@ -721,6 +701,7 @@ esac AC_SUBST(SO_EXT) AC_SUBST(RUN_ERL) +AC_SUBST(CXXNOOPTFLAGS) if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then @@ -737,12 +718,6 @@ CONFIG_STATUS=$WXERL_SYS_TYPE/config.status dnl -if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then - if test X"$WXERL_CAN_BUILD_DRIVER" != X"true" ; then - AC_MSG_ERROR([Cannot build wxErlang driver, see ./CONF_INFO for information]) - fi -fi - AC_CONFIG_FILES([ config.mk c_src/Makefile @@ -750,20 +725,9 @@ AC_CONFIG_FILES([ AC_OUTPUT -if test X"$WX_BUILDING_INSIDE_ERLSRC" != X"true" ; then - AC_MSG_NOTICE() - AC_MSG_NOTICE(--------------------------------------------------) - AC_MSG_NOTICE(Using erlang compiler: [$ERLC]) - AC_MSG_NOTICE(wxErlang Install in: [$ERLANG_ROOT_DIR/lib/wx-$ERLWX_VSN]) - AC_MSG_NOTICE(Erlang driver in priv/[$WXERL_SYS_TYPE]/wxe_driver[$SO_EXT]) - AC_MSG_NOTICE(--------------------------------------------------) -fi - -if test X"$WX_BUILDING_INSIDE_ERLSRC" = X"true" ; then - CORES=`ls core* 2>/dev/null` - if test X"$CORES" != X"" ; then - echo "Configure dumped core files" > ignore_core_files - fi +CORES=`ls core* 2>/dev/null` +if test X"$CORES" != X"" ; then + echo "Configure dumped core files" > ignore_core_files fi diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml index 37973d0dba..9fb4a430e5 100644 --- a/lib/xmerl/doc/src/notes.xml +++ b/lib/xmerl/doc/src/notes.xml @@ -47,6 +47,23 @@ </section> +<section><title>Xmerl 1.3.20.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + <c>xmerl_sax_parser</c> crashed during charset detection + when the xml declarations attribute values was missing + the closing quotation (' or ").</p> + <p> + Own Id: OTP-15826</p> + </item> + </list> + </section> + +</section> + <section><title>Xmerl 1.3.20</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/xmerl/src/xmerl_sax_parser.erl b/lib/xmerl/src/xmerl_sax_parser.erl index fe836fd8cd..2767d02552 100644 --- a/lib/xmerl/src/xmerl_sax_parser.erl +++ b/lib/xmerl/src/xmerl_sax_parser.erl @@ -369,8 +369,8 @@ parse_eq(_, State) -> %%---------------------------------------------------------------------- parse_value(<<C, Rest/binary>>, State) when ?is_whitespace(C) -> parse_value(Rest, State); -parse_value(<<C, Rest/binary>>, _State) when C == $'; C == $" -> - parse_value_1(Rest, C, []); +parse_value(<<C, Rest/binary>>, State) when C == $'; C == $" -> + parse_value_1(Rest, C, [], State); parse_value(_, State) -> ?fatal_error(State, "\', \" or whitespace expected"). @@ -383,10 +383,12 @@ parse_value(_, State) -> %% Rest = binary() %% Description: Parsing an attribute value from the stream. %%---------------------------------------------------------------------- -parse_value_1(<<Stop, Rest/binary>>, Stop, Acc) -> +parse_value_1(<<Stop, Rest/binary>>, Stop, Acc, _State) -> {lists:reverse(Acc), Rest}; -parse_value_1(<<C, Rest/binary>>, Stop, Acc) -> - parse_value_1(Rest, Stop, [C |Acc]). +parse_value_1(<<C, Rest/binary>>, Stop, Acc, State) -> + parse_value_1(Rest, Stop, [C |Acc], State); +parse_value_1(_, _Stop, _Acc, State) -> + ?fatal_error(State, "end of input and no \' or \" found"). %%====================================================================== %% Default functions |