diff options
Diffstat (limited to 'lib')
54 files changed, 1463 insertions, 991 deletions
diff --git a/lib/compiler/src/v3_codegen.erl b/lib/compiler/src/v3_codegen.erl index e9152ba88f..d7a7778740 100644 --- a/lib/compiler/src/v3_codegen.erl +++ b/lib/compiler/src/v3_codegen.erl @@ -79,13 +79,9 @@ function(#k_fdef{anno=#k{a=Anno},func=Name,arity=Arity, try #k_match{} = Kb, %Assertion. - %% Try to suppress the stack frame unless it is - %% really needed. - Body0 = avoid_stack_frame(Kb), - %% Annotate kernel records with variable usage. Vdb0 = init_vars(As), - {Body,_,Vdb} = body(Body0, 1, Vdb0), + {Body,_,Vdb} = body(Kb, 1, Vdb0), %% Generate the BEAM assembly code. {Asm,EntryLabel,St} = cg_fun(Body, As, Vdb, AtomMod, @@ -98,136 +94,6 @@ function(#k_fdef{anno=#k{a=Anno},func=Name,arity=Arity, erlang:raise(Class, Error, Stack) end. - -%% avoid_stack_frame(Kernel) -> Kernel' -%% If possible, avoid setting up a stack frame. Functions -%% that only do matching, calls to guard BIFs, and tail-recursive -%% calls don't need a stack frame. - -avoid_stack_frame(#k_match{body=Body}=M) -> - try - M#k_match{body=avoid_stack_frame_1(Body)} - catch - impossible -> - M - end. - -avoid_stack_frame_1(#k_alt{first=First0,then=Then0}=Alt) -> - First = avoid_stack_frame_1(First0), - Then = avoid_stack_frame_1(Then0), - Alt#k_alt{first=First,then=Then}; -avoid_stack_frame_1(#k_bif{op=Op}=Bif) -> - case Op of - #k_internal{} -> - %% Most internal BIFs clobber the X registers. - throw(impossible); - _ -> - Bif - end; -avoid_stack_frame_1(#k_break{anno=Anno,args=Args}) -> - #k_guard_break{anno=Anno,args=Args}; -avoid_stack_frame_1(#k_guard_break{}=Break) -> - Break; -avoid_stack_frame_1(#k_enter{}=Enter) -> - %% Tail-recursive calls don't need a stack frame. - Enter; -avoid_stack_frame_1(#k_guard{clauses=Cs0}=Guard) -> - Cs = avoid_stack_frame_list(Cs0), - Guard#k_guard{clauses=Cs}; -avoid_stack_frame_1(#k_guard_clause{guard=G0,body=B0}=C) -> - G = avoid_stack_frame_1(G0), - B = avoid_stack_frame_1(B0), - C#k_guard_clause{guard=G,body=B}; -avoid_stack_frame_1(#k_match{anno=A,vars=Vs,body=B0,ret=Ret}) -> - %% Use #k_guard_match{} instead to avoid saving the X registers - %% to the stack before matching. - B = avoid_stack_frame_1(B0), - #k_guard_match{anno=A,vars=Vs,body=B,ret=Ret}; -avoid_stack_frame_1(#k_guard_match{body=B0}=M) -> - B = avoid_stack_frame_1(B0), - M#k_guard_match{body=B}; -avoid_stack_frame_1(#k_protected{arg=Arg0}=Prot) -> - Arg = avoid_stack_frame_1(Arg0), - Prot#k_protected{arg=Arg}; -avoid_stack_frame_1(#k_put{}=Put) -> - Put; -avoid_stack_frame_1(#k_return{}=Ret) -> - Ret; -avoid_stack_frame_1(#k_select{var=#k_var{anno=Vanno},types=Types0}=Select) -> - case member(reuse_for_context, Vanno) of - false -> - Types = avoid_stack_frame_list(Types0), - Select#k_select{types=Types}; - true -> - %% Including binary patterns that overwrite the register containing - %% the binary with the match context may not be safe. For example, - %% bs_match_SUITE:bin_tail_e/1 with inlining will be rejected by - %% beam_validator. - %% - %% Essentially the following code is produced: - %% - %% bs_match {x,0} => {x,0} - %% ... - %% bs_match {x,0} => {x,1} %% ILLEGAL - %% - %% A bs_match instruction will only accept a match context as the - %% source operand if the source and destination registers are the - %% the same (as in the first bs_match instruction above). - %% The second bs_match instruction is therefore illegal. - %% - %% This situation is avoided if there is a stack frame: - %% - %% move {x,0} => {y,0} - %% bs_match {x,0} => {x,0} - %% ... - %% bs_match {y,0} => {x,1} %% LEGAL - %% - throw(impossible) - end; -avoid_stack_frame_1(#k_seq{arg=#k_call{anno=Anno,op=Op}=Call, - body=#k_break{args=BrArgs0}}=Seq) -> - case Op of - #k_remote{mod=#k_atom{val=Mod}, - name=#k_atom{val=Name}, - arity=Arity} -> - case erl_bifs:is_exit_bif(Mod, Name, Arity) of - false -> - %% Will clobber X registers. Must have a stack frame. - throw(impossible); - true -> - %% The call to this BIF will never return. It is safe - %% to suppress the stack frame. - Bif = #k_bif{anno=Anno, - op=#k_internal{name=guard_error,arity=1}, - args=[Call],ret=[]}, - BrArgs = lists:duplicate(length(BrArgs0), #k_nil{}), - GB = #k_guard_break{anno=#k{us=[],ns=[],a=[]},args=BrArgs}, - Seq#k_seq{arg=Bif,body=GB} - end; - _ -> - %% Will clobber X registers. Must have a stack frame. - throw(impossible) - end; -avoid_stack_frame_1(#k_seq{arg=A0,body=B0}=Seq) -> - A = avoid_stack_frame_1(A0), - B = avoid_stack_frame_1(B0), - Seq#k_seq{arg=A,body=B}; -avoid_stack_frame_1(#k_test{}=Test) -> - Test; -avoid_stack_frame_1(#k_type_clause{values=Values0}=TC) -> - Values = avoid_stack_frame_list(Values0), - TC#k_type_clause{values=Values}; -avoid_stack_frame_1(#k_val_clause{body=B0}=VC) -> - B = avoid_stack_frame_1(B0), - VC#k_val_clause{body=B}; -avoid_stack_frame_1(_Body) -> - throw(impossible). - -avoid_stack_frame_list([H|T]) -> - [avoid_stack_frame_1(H)|avoid_stack_frame_list(T)]; -avoid_stack_frame_list([]) -> []. - - %% This pass creates beam format annotated with variable lifetime %% information. Each thing is given an index and for each variable we %% store the first and last index for its occurrence. The variable diff --git a/lib/compiler/test/match_SUITE.erl b/lib/compiler/test/match_SUITE.erl index e3f842b668..72e5356a8d 100644 --- a/lib/compiler/test/match_SUITE.erl +++ b/lib/compiler/test/match_SUITE.erl @@ -378,6 +378,13 @@ untuplify(Config) when is_list(Config) -> %% We do this to cover sys_core_fold:unalias_pat/1. {1,2,3,4,alias,{[1,2],{3,4},alias}} = untuplify_1([1,2], {3,4}, alias), error = untuplify_1([1,2], {3,4}, 42), + + %% Test that a previous bug in v3_codegen is gone. (The sinking of + %% stack frames into only the case arms that needed them was not always + %% safe.) + [33, -1, -33, 1] = untuplify_2(32, 65), + {33, 1, -33, -1} = untuplify_2(65, 32), + ok. untuplify_1(A, B, C) -> @@ -390,6 +397,21 @@ untuplify_1(A, B, C) -> error end. +untuplify_2(V1, V2) -> + {D1,D2,D3,D4} = + if V1 > V2 -> + %% The 1 value was overwritten by the value of V2-V1. + {V1-V2, 1, V2-V1, -1}; + true -> + {V2-V1, -1, V1-V2, 1} + end, + if + D2 > D4 -> + {D1, D2, D3, D4}; + true -> + [D1, D2, D3, D4] + end. + %% Coverage of beam_dead:shortcut_boolean_label/4. shortcut_boolean(Config) when is_list(Config) -> false = shortcut_boolean_1([0]), diff --git a/lib/crypto/test/Makefile b/lib/crypto/test/Makefile index e046a25338..8b320e01a9 100644 --- a/lib/crypto/test/Makefile +++ b/lib/crypto/test/Makefile @@ -6,6 +6,7 @@ include $(ERL_TOP)/make/$(TARGET)/otp.mk # ---------------------------------------------------- MODULES = \ + crypto_bench_SUITE \ blowfish_SUITE \ crypto_SUITE \ engine_SUITE @@ -77,7 +78,7 @@ release_spec: release_tests_spec: $(TEST_TARGET) $(INSTALL_DIR) "$(RELSYSDIR)" - $(INSTALL_DATA) crypto.spec crypto.cover $(RELTEST_FILES) "$(RELSYSDIR)" + $(INSTALL_DATA) crypto.spec crypto_bench.spec crypto.cover $(RELTEST_FILES) "$(RELSYSDIR)" @tar cfh - *_SUITE_data | (cd "$(RELSYSDIR)"; tar xf -) chmod -R u+w "$(RELSYSDIR)" diff --git a/lib/crypto/test/crypto.spec b/lib/crypto/test/crypto.spec index cc09970cb3..4a95275687 100644 --- a/lib/crypto/test/crypto.spec +++ b/lib/crypto/test/crypto.spec @@ -1 +1,6 @@ {suites,"../crypto_test",all}. + +{skip_suites, "../crypto_test", [crypto_bench_SUITE + ], + "Benchmarks run separately"}. + diff --git a/lib/crypto/test/crypto_SUITE.erl b/lib/crypto/test/crypto_SUITE.erl index 6c6188f775..98de1d7700 100644 --- a/lib/crypto/test/crypto_SUITE.erl +++ b/lib/crypto/test/crypto_SUITE.erl @@ -156,7 +156,7 @@ groups() -> ]}, {dh, [], [generate_compute, compute_bug]}, - {ecdh, [], [generate_all_supported, compute, generate]}, + {ecdh, [], [use_all_elliptic_curves, compute, generate]}, {srp, [], [generate_compute]}, {des_cbc, [], [block]}, {des_cfb, [], [block]}, @@ -563,32 +563,43 @@ compute(Config) when is_list(Config) -> Gen = proplists:get_value(compute, Config), lists:foreach(fun do_compute/1, Gen). %%-------------------------------------------------------------------- -generate_all_supported() -> - [{doc, " Test that all curves from crypto:ec_curves/0 returns two binaries"}]. -generate_all_supported(_Config) -> +use_all_elliptic_curves() -> + [{doc, " Test that all curves from crypto:ec_curves/0"}]. +use_all_elliptic_curves(_Config) -> + Msg = <<"hello world!">>, + Sups = crypto:supports(), + Curves = proplists:get_value(curves, Sups), + Hashs = proplists:get_value(hashs, Sups), + ct:log("Lib: ~p~nFIPS: ~p~nCurves:~n~p~nHashs: ~p", [crypto:info_lib(), + crypto:info_fips(), + Curves, + Hashs]), Results = - [try - crypto:generate_key(ecdh, C) - of - {B1,B2} when is_binary(B1) and is_binary(B2) -> - %% That is, seems like it works as expected. - {ok,C}; - Err -> - ct:log("ERROR: Curve ~p generated ~p", [C,Err]), - {error,{C,Err}} - catch - Cls:Err:Stack -> - ct:log("ERROR: Curve ~p exception ~p:~p~n~p", [C,Cls,Err,Stack]), - {error,{C,{Cls,Err}}} - end - || C <- crypto:ec_curves(), - not lists:member(C, [ed25519, ed448]) + [{{Curve,Hash}, + try + {Pub,Priv} = crypto:generate_key(ecdh, Curve), + true = is_binary(Pub), + true = is_binary(Priv), + Sig = crypto:sign(ecdsa, Hash, Msg, [Priv, Curve]), + crypto:verify(ecdsa, Hash, Msg, Sig, [Pub, Curve]) + catch + C:E -> + {C,E} + end} + || Curve <- Curves -- [ed25519, ed448, x25519, x448, ipsec3, ipsec4], + Hash <- Hashs -- [md4, md5, ripemd160, sha3_224, sha3_256, sha3_384, sha3_512] ], - OK = [C || {ok,C} <- Results], - ct:log("Ok (len=~p): ~p", [length(OK), OK]), - false = lists:any(fun({error,_}) -> true; - (_) -> false - end, Results). + Fails = + lists:filter(fun({_,true}) -> false; + (_) -> true + end, Results), + case Fails of + [] -> + ok; + _ -> + ct:log("Fails:~n~p",[Fails]), + ct:fail("Bad curve(s)",[]) + end. %%-------------------------------------------------------------------- generate() -> diff --git a/lib/crypto/test/crypto_bench.spec b/lib/crypto/test/crypto_bench.spec new file mode 100644 index 0000000000..b9a26d94db --- /dev/null +++ b/lib/crypto/test/crypto_bench.spec @@ -0,0 +1,3 @@ +{suites, "../crypto_test", [ + crypto_bench_SUITE + ]}. diff --git a/lib/crypto/test/crypto_bench_SUITE.erl b/lib/crypto/test/crypto_bench_SUITE.erl new file mode 100644 index 0000000000..e1fd0a63e5 --- /dev/null +++ b/lib/crypto/test/crypto_bench_SUITE.erl @@ -0,0 +1,387 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2009-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(crypto_bench_SUITE). + +%% Note: This directive should only be used in test suites. +-compile(export_all). + +-include_lib("common_test/include/ct_event.hrl"). +-include_lib("common_test/include/ct.hrl"). + +suite() -> [%%{ct_hooks,[{ts_install_cth,[{nodenames,2}]}]}, + {timetrap,{minutes,2}} + ]. + +all() -> + [ + {group, ciphers_128} + ]. + +groups() -> + [ + {ciphers_128, [{repeat, 3}], [{group,textblock_256} + ]}, + + {textblock_256, [{repeat,2}], [ + block, + stream + ]} + ]. + +%%%---------------------------------------------------------------- +%%% +init_per_suite(Config) -> + try crypto:start() of + _ -> + [{_,_,Info}] = crypto:info_lib(), + ct:comment("~s",[Info]), + ct:pal("Crypto version: ~p~n~n~p",[Info,crypto:supports()]), + [{sec_goal,5} | Config] + catch _:_ -> + {fail, "Crypto did not start"} + end. + +end_per_suite(_Config) -> + application:stop(crypto). + +%%%---------------------------------------------------------------- +%%% +init_per_group(Group, Config0) -> + ct:pal("~p(~p,..)",[?FUNCTION_NAME,Group]), + + Config = calibrate(Config0), + case atom_to_list(Group) of + "ciphers_"++KeySizeStr -> + KeySize = list_to_integer(KeySizeStr), + [{key_size,KeySize} + | measure_openssl_aes_cbc(KeySize, Config)]; + + "textblock_"++BlockSizeStr -> + BlockSize = list_to_integer(BlockSizeStr), + [{block_size,BlockSize} | Config]; + + _ -> + Config + end. + +end_per_group(_Group, Config) -> + Config. + + +measure_openssl_aes_cbc(KeySize, Config) -> + BLno_acc = [baseline(aes_cbc, KeySize, false)], + ct:pal("Non-accelerated baseline encryption time [µs/block]:~n~p", [BLno_acc]), + BLacc = [baseline(aes_cbc, KeySize, true)], + ct:pal("Possibly accelerated baseline encryption time [µs/block]:~n~p", [BLacc]), + [{acc,BLacc}, + {no_acc,BLno_acc} | Config]. + +calibrate(Config) -> + Secs = proplists:get_value(sec_goal, Config, 5), + {_,Empty} = data(empty, 0, 0), + {Ne,Te} = run1(Secs*2000, Empty), + [{overhead,Te/Ne} | Config]. + +%%%================================================================ +%%% +%%% +block(Config) -> + run_cryptos([aes_cbc, aes_gcm, aes_ccm, chacha20_poly1305], + Config). + +stream(Config) -> + run_cryptos([aes_ctr, chacha20], + Config). + +%%%================================================================ +%%% +%%% + +run_cryptos(Cryptos, Config) -> + run_cryptos(Cryptos, 1, Config). + +run_cryptos(Cryptos, Factor, Config) -> + KeySize = proplists:get_value(key_size, Config), + BlockSize = proplists:get_value(block_size, Config), + MilliSecGoal = 1000*proplists:get_value(sec_goal,Config), + OverHead = proplists:get_value(overhead, Config, 0), + [try + Factor*run(Crypto,KeySize,BlockSize,MilliSecGoal) - OverHead + of + TimePerOp -> % µs + %% First, Report speed of encrypting blocks of 1000. [blocks/sec] + ReportUnit = 1000, + Label = [fmt(Crypto)," key:",KeySize," block:",BlockSize], + report(Label, + (BlockSize/ReportUnit)*1000000/TimePerOp + ), + + EffCrypto = case Crypto of + X -> X + end, + %% Percent of accelerated speed + case find_value([acc,{EffCrypto,KeySize},BlockSize], Config) of + undefined -> + ok; + TimePerOpBaseAcc -> + report(["Percent of acc OpenSSL "|Label], + 100*TimePerOpBaseAcc/TimePerOp % Percent of base *speed* + ) + end, + + %% Percent of non-accelerated speed + case find_value([no_acc,{EffCrypto,KeySize},BlockSize], Config) of + undefined -> + ok; + TimePerOpBaseNoAcc -> + report(["Percent of noacc OpenSSL "|Label], + 100*TimePerOpBaseNoAcc/TimePerOp % Percent of base *speed* + ) + end + catch + _:_ -> + ct:pal("~p unsupported",[{Crypto,KeySize,BlockSize}]) + end + || Crypto <- Cryptos, + supported(Crypto) + ]. + + +run(Crypto, KeySize, BlockSize, MilliSecGoal) -> + {_Type, Funs} = data(Crypto, KeySize, BlockSize), + {Nc,Tc} = run1(MilliSecGoal, Funs), + Tc/Nc. + +fmt(X) -> X. + + +find_value(KeyPath, PropList, Default) -> + try find_value(KeyPath, PropList) + of + undefined -> Default + catch + error:function_clause -> Default + end. + +find_value(KeyPath, PropList) -> + lists:foldl(fun(K, L) when is_list(L) -> proplists:get_value(K,L); + (_, _) -> undefined + end, PropList, KeyPath). + +%%%================================================================ +%%% +%%% +funs({block, {Type, Key, IV, Block}}) -> + {fun() -> ok end, + fun(_) -> crypto:block_encrypt(Type, Key, IV, Block) end, + fun(_) -> ok end}; + +funs({stream, {Type, Key, IV, Block}}) -> + {fun() -> {crypto:stream_init(Type, Key, IV),ok} end, + fun({Ctx,_}) -> crypto:stream_encrypt(Ctx, Block) end, + fun(_) -> ok end}. + + +data(aes_cbc, KeySize, BlockSize) -> + Type = case KeySize of + 128 -> aes_cbc128; + 256 -> aes_cbc256 + end, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + Block = mk_bin(BlockSize), + {Type, funs({block, {Type, Key, IV, Block}})}; + +data(aes_gcm, KeySize, BlockSize) -> + Type = aes_gcm, + Key = mk_bin(KeySize div 8), + IV = mk_bin(12), + Block = mk_bin(BlockSize), + AAD = <<01,02,03,04>>, + {Type, funs({block, {Type, Key, IV, {AAD,Block,16}}})}; + +data(aes_ccm, KeySize, BlockSize) -> + Type = aes_ccm, + Key = mk_bin(KeySize div 8), + IV = mk_bin(12), + Block = mk_bin(BlockSize), + AAD = <<01,02,03,04>>, + {Type, funs({block, {Type, Key, IV, {AAD,Block,12}}})}; + +data(aes_ctr, KeySize, BlockSize) -> + Type = aes_ctr, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + Block = mk_bin(BlockSize), + {Type, funs({stream, {Type, Key, IV, Block}})}; + +data(chacha20_poly1305, 256=KeySize, BlockSize) -> + Type = chacha20_poly1305, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + AAD = <<01,02,03,04>>, + Block = mk_bin(BlockSize), + {Type, funs({block, {Type, Key, IV, {AAD,Block}}})}; + +data(chacha20, 256=KeySize, BlockSize) -> + Type = chacha20, + Key = mk_bin(KeySize div 8), + IV = mk_bin(16), + Block = mk_bin(BlockSize), + {Type, funs({stream, {Type, Key, IV, Block}})}; + +data(empty, 0, 0) -> + {undefined, + {fun() -> ok end, + fun(X) -> X end, + fun(_) -> ok end}}. + +%%%================================================================ +%%% +%%% +run1(MilliSecGoal, Funs) -> + Parent = self(), + Pid = spawn(fun() -> + {Fi,Fu,Ff} = Funs, + Ctx0 = Fi(), + T0 = start_time(), + {N,Ctx} = loop(Fu, Ctx0, 0), + T = elapsed_time(T0), + Ff(Ctx), + Parent ! {result,N,microseconds(T)} + end), + Pid ! go, + receive + after MilliSecGoal -> + Pid ! stop + end, + receive + {result,N,MicroSecs} -> + {N,MicroSecs} + end. + + +loop(F, Ctx, N) -> + receive + stop -> + {N, Ctx} + after 0 -> + loop(F, F(Ctx), N+1) + end. + +%%%---------------------------------------------------------------- +report(LabelList, Value) -> + Label = report_chars(lists:concat(LabelList)), + ct:pal("ct_event:notify ~p: ~p", [Label, Value]), + ct_event:notify( + #event{name = benchmark_data, + data = [{name, Label}, + {value,Value}]}). + +report_chars(Cs) -> + [case C of + $- -> $_; + _ -> C + end || C <- Cs]. + +%%%---------------------------------------------------------------- +supported(Algorithm) -> + lists:member(Algorithm, + [A || {_,As} <- crypto:supports(), A <- As] + ). + +%%%---------------------------------------------------------------- +start_time() -> + erlang:system_time(). + +elapsed_time(StartTime) -> + erlang:system_time() - StartTime. + +microseconds(Time) -> + erlang:convert_time_unit(Time, native, microsecond). + +%%%---------------------------------------------------------------- + +%% Example output: +%% +DT:aes-128-cbc:3:16 +%% +R:135704772:aes-128-cbc:2.980000 +%% +DT:aes-128-cbc:3:64 +%% +R:36835089:aes-128-cbc:3.000000 +%% +DT:aes-128-cbc:3:256 +%% +R:9398616:aes-128-cbc:3.000000 +%% +DT:aes-128-cbc:3:1024 +%% +R:2355683:aes-128-cbc:2.990000 +%% +DT:aes-128-cbc:3:8192 +%% +R:294508:aes-128-cbc:2.990000 +%% +H:16:64:256:1024:8192 +%% +F:22:aes-128-cbc:728616225.50:785815232.00:802015232.00:806762338.46:806892821.40 + +baseline(Crypto, KeySize, EVP) -> + Spec= + case {Crypto,KeySize} of + {aes_cbc, 128} -> "aes-128-cbc"; + {aes_cbc, 256} -> "aes-256-cbc" + end, + {{Crypto,KeySize}, baseline(Spec, EVP)}. + +baseline(Spec, EVP) -> + Cmd = + case EVP of + true -> "openssl speed -mr -evp " ++ Spec; + false-> "openssl speed -mr " ++ Spec + end, + get_base_values(string:tokens(os:cmd(Cmd),"\n"), Spec, []). + + +get_base_values(["+DT:"++Sdt, + "+R:"++Sr + |T], Crypto, Acc) -> + [Crypto0,_GoalSecs0,BlockSize0] = string:tokens(Sdt, ":"), + [Nblocks0,Crypto0,RealSecs0] = string:tokens(Sr, ":"), + Crypto = fix_possible_space_bug(Crypto0), + RealSecs = list_to_float(RealSecs0), + BlockSize = list_to_integer(BlockSize0), + Nblocks = list_to_integer(Nblocks0), + get_base_values(T, Crypto, [{BlockSize, 1000000*RealSecs/Nblocks} | Acc]); + +get_base_values([_|T], Crypto, Acc) -> + get_base_values(T, Crypto, Acc); + +get_base_values([], _, Acc) -> + lists:sort(Acc). + +fix_possible_space_bug(S) -> lists:concat(lists:join("-",string:tokens(S,"- "))). + +%%%---------------------------------------------------------------- +mk_bin(Size) when Size =< 256 -> + list_to_binary(lists:seq(0,Size-1)); + +mk_bin(Size) when 1024 =< Size -> + B = mk_bin(Size div 4), + Brest = mk_bin(Size rem 4), + <<B/binary, B/binary, B/binary, B/binary, Brest/binary>>; + +mk_bin(Size) when 256 < Size -> + B = mk_bin(Size div 2), + Brest = mk_bin(Size rem 2), + <<B/binary, B/binary, Brest/binary>>. + diff --git a/lib/inets/doc/src/httpd_util.xml b/lib/inets/doc/src/httpd_util.xml index 29971ba8ae..e0f947f860 100644 --- a/lib/inets/doc/src/httpd_util.xml +++ b/lib/inets/doc/src/httpd_util.xml @@ -45,8 +45,7 @@ <fsummary>Converts the date to the Erlang date format.</fsummary> <type> <v>DateString = string()</v> - <v>ErlDate = {{Year,Month,Date},{Hour,Min,Sec}}</v> - <v>Year = Month = Date = Hour = Min = Sec = integer()</v> + <v>ErlDate = calendar:datetime() </v> </type> <desc> <p><c>convert_request_date/1</c> converts <c>DateString</c> to @@ -281,10 +280,10 @@ <func> <name since="">rfc1123_date() -> RFC1123Date</name> - <name since="">rfc1123_date({{YYYY,MM,DD},{Hour,Min,Sec}}) -> RFC1123Date</name> + <name since="">rfc1123_date(Date) -> RFC1123Date</name> <fsummary>Returns the current date in RFC 1123 format.</fsummary> <type> - <v>YYYY = MM = DD = Hour = Min = Sec = integer()</v> + <v> Date = calendar:datetime()</v> <v>RFC1123Date = string()</v> </type> <desc> diff --git a/lib/kernel/test/init_SUITE.erl b/lib/kernel/test/init_SUITE.erl index 6a006cdc01..4f90260f98 100644 --- a/lib/kernel/test/init_SUITE.erl +++ b/lib/kernel/test/init_SUITE.erl @@ -295,7 +295,7 @@ is_real_system(KernelVsn, StdlibVsn) -> %% before restart. %% ------------------------------------------------ many_restarts() -> - [{timetrap,{minutes,8}}]. + [{timetrap,{minutes,16}}]. many_restarts(Config) when is_list(Config) -> {ok, Node} = loose_node:start(init_test, "", ?DEFAULT_TIMEOUT_SEC), @@ -315,7 +315,7 @@ loop_restart(N,Node,EHPid) -> loose_node:stop(Node), ct:fail(not_stopping) end, - ok = wait_for(30, Node, EHPid), + ok = wait_for(60, Node, EHPid), loop_restart(N-1,Node,rpc:call(Node,erlang,whereis,[logger])). wait_for(0,Node,_) -> @@ -367,7 +367,8 @@ restart(Config) when is_list(Config) -> SysProcs0 = rpc:call(Node, ?MODULE, find_system_processes, []), io:format("SysProcs0=~p~n", [SysProcs0]), [InitPid, PurgerPid, LitCollectorPid, - DirtySigNPid, DirtySigHPid, DirtySigMPid] = SysProcs0, + DirtySigNPid, DirtySigHPid, DirtySigMPid, + PrimFilePid] = SysProcs0, InitPid = rpc:call(Node, erlang, whereis, [init]), PurgerPid = rpc:call(Node, erlang, whereis, [erts_code_purger]), Procs = rpc:call(Node, erlang, processes, []), @@ -385,7 +386,8 @@ restart(Config) when is_list(Config) -> SysProcs1 = rpc:call(Node, ?MODULE, find_system_processes, []), io:format("SysProcs1=~p~n", [SysProcs1]), [InitPid1, PurgerPid1, LitCollectorPid1, - DirtySigNPid1, DirtySigHPid1, DirtySigMPid1] = SysProcs1, + DirtySigNPid1, DirtySigHPid1, DirtySigMPid1, + PrimFilePid1] = SysProcs1, %% Still the same init process! InitPid1 = rpc:call(Node, erlang, whereis, [init]), @@ -411,6 +413,10 @@ restart(Config) when is_list(Config) -> DirtySigMP = pid_to_list(DirtySigMPid), DirtySigMP = pid_to_list(DirtySigMPid1), + %% and same prim_file helper process! + PrimFileP = pid_to_list(PrimFilePid), + PrimFileP = pid_to_list(PrimFilePid1), + NewProcs0 = rpc:call(Node, erlang, processes, []), NewProcs = NewProcs0 -- SysProcs1, case check_processes(NewProcs, MaxPid) of @@ -437,7 +443,8 @@ restart(Config) when is_list(Config) -> literal_collector, dirty_sig_handler_normal, dirty_sig_handler_high, - dirty_sig_handler_max}). + dirty_sig_handler_max, + prim_file}). find_system_processes() -> find_system_procs(processes(), #sys_procs{}). @@ -448,7 +455,8 @@ find_system_procs([], SysProcs) -> SysProcs#sys_procs.literal_collector, SysProcs#sys_procs.dirty_sig_handler_normal, SysProcs#sys_procs.dirty_sig_handler_high, - SysProcs#sys_procs.dirty_sig_handler_max]; + SysProcs#sys_procs.dirty_sig_handler_max, + SysProcs#sys_procs.prim_file]; find_system_procs([P|Ps], SysProcs) -> case process_info(P, [initial_call, priority]) of [{initial_call,{otp_ring0,start,2}},_] -> @@ -472,6 +480,9 @@ find_system_procs([P|Ps], SysProcs) -> {priority,max}] -> undefined = SysProcs#sys_procs.dirty_sig_handler_max, find_system_procs(Ps, SysProcs#sys_procs{dirty_sig_handler_max = P}); + [{initial_call,{prim_file,start,0}},_] -> + undefined = SysProcs#sys_procs.prim_file, + find_system_procs(Ps, SysProcs#sys_procs{prim_file = P}); _ -> find_system_procs(Ps, SysProcs) end. diff --git a/lib/observer/test/crashdump_helper.erl b/lib/observer/test/crashdump_helper.erl index d5d3649525..d6b5eff9b5 100644 --- a/lib/observer/test/crashdump_helper.erl +++ b/lib/observer/test/crashdump_helper.erl @@ -204,5 +204,4 @@ dump_persistent_terms() -> create_persistent_terms() -> persistent_term:put({?MODULE,first}, {pid,42.0}), persistent_term:put({?MODULE,second}, [1,2,3]), - persistent_term:get(). - + {persistent_term:get({?MODULE,first}),persistent_term:get({?MODULE,second})}. diff --git a/lib/observer/test/crashdump_viewer_SUITE.erl b/lib/observer/test/crashdump_viewer_SUITE.erl index 8c5e618f4a..31cf7011d4 100644 --- a/lib/observer/test/crashdump_viewer_SUITE.erl +++ b/lib/observer/test/crashdump_viewer_SUITE.erl @@ -615,9 +615,8 @@ special(File,Procs) -> #proc{dict=Dict} = ProcDetails, %% io:format("~p\n", [Dict]), - Pts1 = crashdump_helper:create_persistent_terms(), - Pts2 = proplists:get_value(pts,Dict), - true = lists:sort(Pts1) =:= lists:sort(Pts2), + Pts = crashdump_helper:create_persistent_terms(), + Pts = proplists:get_value(pts,Dict), io:format(" persistent terms ok",[]), ok; _ -> diff --git a/lib/odbc/c_src/odbcserver.c b/lib/odbc/c_src/odbcserver.c index 8c799f6ff1..fb4f61417e 100644 --- a/lib/odbc/c_src/odbcserver.c +++ b/lib/odbc/c_src/odbcserver.c @@ -2749,6 +2749,11 @@ static diagnos get_diagnos(SQLSMALLINT handleType, SQLHANDLE handle, Boolean ext errmsg_buffer_size = errmsg_buffer_size - errmsg_size; acc_errmsg_size = acc_errmsg_size + errmsg_size; current_errmsg_pos = current_errmsg_pos + errmsg_size; + } else if(result == SQL_SUCCESS_WITH_INFO && errmsg_size >= errmsg_buffer_size) { + memcpy(diagnos.sqlState, current_sql_state, SQL_STATE_SIZE); + diagnos.nativeError = nativeError; + acc_errmsg_size = errmsg_buffer_size; + break; } else { break; } diff --git a/lib/runtime_tools/examples/dist.systemtap b/lib/runtime_tools/examples/dist.systemtap index bb20d617e1..4102a5243c 100644 --- a/lib/runtime_tools/examples/dist.systemtap +++ b/lib/runtime_tools/examples/dist.systemtap @@ -19,18 +19,18 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("dist-monitor") +probe process("beam.smp").mark("dist-monitor") { printf("monitor: pid %d, who %s, what %s, node %s, type %s, reason %s\n", pid(), @@ -38,38 +38,38 @@ probe process("beam").mark("dist-monitor") user_string($arg5)); } -probe process("beam").mark("dist-port_busy") +probe process("beam.smp").mark("dist-port_busy") { printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); - blocked_procs[user_string($arg4)] = timestamp; + blocked_procs[user_string($arg4)] = local_clock_ns(); } -probe process("beam").mark("dist-port_busy") +probe process("beam.smp").mark("dist-port_busy") { printf("dist port_busy: node %s, port %s, remote_node %s, blocked pid %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); - blocked_procs[user_string($arg4)] = timestamp; + blocked_procs[user_string($arg4)] = local_clock_ns(); } -probe process("beam").mark("dist-output") +probe process("beam.smp").mark("dist-output") { printf("dist output: node %s, port %s, remote_node %s bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4); } -probe process("beam").mark("dist-outputv") +probe process("beam.smp").mark("dist-outputv") { printf("port outputv: node %s, port %s, remote_node %s bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4); } -probe process("beam").mark("process-scheduled") +probe process("beam.smp").mark("process-scheduled") { pidstr = user_string($arg1); if (pidstr in blocked_procs) { printf("blocked pid %s scheduled now, waited %d microseconds\n", - pidstr, (timestamp - blocked_procs[pidstr]) / 1000); + pidstr, (local_clock_ns() - blocked_procs[pidstr]) / 1000); delete blocked_procs[pidstr]; } } diff --git a/lib/runtime_tools/examples/driver1.systemtap b/lib/runtime_tools/examples/driver1.systemtap index e1ee8ecffc..f5bc28b42d 100644 --- a/lib/runtime_tools/examples/driver1.systemtap +++ b/lib/runtime_tools/examples/driver1.systemtap @@ -19,108 +19,102 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("driver-init") +probe process("beam.smp").mark("driver__init") { printf("driver init name %s major %d minor %d flags %d\n", user_string($arg1), $arg2, $arg3, $arg4); } -probe process("beam").mark("driver-start") +probe process("beam.smp").mark("driver__start") { printf("driver start pid %s driver name %s port %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-stop") +probe process("beam.smp").mark("driver__stop") { printf("driver stop pid %s driver name %s port %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-finish") +probe process("beam.smp").mark("driver__finish") { printf("driver finish driver name %s\n", user_string($arg1)); } -probe process("beam").mark("driver-flush") +probe process("beam.smp").mark("driver__flush") { printf("driver flush pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-output") +probe process("beam.smp").mark("driver__output") { printf("driver output pid %s port %s port name %s bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4); } -probe process("beam").mark("driver-outputv") +probe process("beam.smp").mark("driver__outputv") { printf("driver outputv pid %s port %s port name %s bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4); } -probe process("beam").mark("driver-control") +probe process("beam.smp").mark("driver__control") { printf("driver control pid %s port %s port name %s command %d bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4, $arg5); } -probe process("beam").mark("driver-call") +probe process("beam.smp").mark("driver__call") { printf("driver call pid %s port %s port name %s command %d bytes %d\n", user_string($arg1), user_string($arg2), user_string($arg3), $arg4, $arg5); } -probe process("beam").mark("driver-event") -{ - printf("driver event pid %s port %s port name %s\n", - user_string($arg1), user_string($arg2), user_string($arg3)); -} - -probe process("beam").mark("driver-ready_input") +probe process("beam.smp").mark("driver__ready_input") { printf("driver ready_input pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-ready_output") +probe process("beam.smp").mark("driver__ready_output") { printf("driver ready_output pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-timeout") +probe process("beam.smp").mark("driver__timeout") { printf("driver timeout pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-ready_async") +probe process("beam.smp").mark("driver__ready_async") { printf("driver ready_async pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-process_exit") +probe process("beam.smp").mark("driver__process_exit") { printf("driver process_exit pid %s port %s port name %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("driver-stop_select") +probe process("beam.smp").mark("driver__stop_select") { printf("driver stop_select driver name %s\n", user_string($arg1)); } diff --git a/lib/runtime_tools/examples/function-calls.systemtap b/lib/runtime_tools/examples/function-calls.systemtap index 9c44b2d014..6bb173b3ec 100644 --- a/lib/runtime_tools/examples/function-calls.systemtap +++ b/lib/runtime_tools/examples/function-calls.systemtap @@ -18,51 +18,51 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("local-function-entry") +probe process("beam.smp").mark("local-function-entry") { printf("pid %s enter (local) %s depth %d\n", user_string($arg1), user_string($arg2), $arg3); } -probe process("beam").mark("global-function-entry") +probe process("beam.smp").mark("global-function-entry") { printf("pid %s enter (global) %s depth %d\n", user_string($arg1), user_string($arg2), $arg3); } -probe process("beam").mark("function-return") +probe process("beam.smp").mark("function-return") { printf("pid %s return %s depth %d\n", user_string($arg1), user_string($arg2), $arg3); } -probe process("beam").mark("bif-entry") +probe process("beam.smp").mark("bif-entry") { printf("pid %s BIF entry mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("bif-return") +probe process("beam.smp").mark("bif-return") { printf("pid %s BIF return mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("nif-entry") +probe process("beam.smp").mark("nif-entry") { printf("pid %s NIF entry mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("nif-return") +probe process("beam.smp").mark("nif-return") { printf("pid %s NIF return mfa %s\n", user_string($arg1), user_string($arg2)); } diff --git a/lib/runtime_tools/examples/garbage-collection.systemtap b/lib/runtime_tools/examples/garbage-collection.systemtap index e414eea821..14f0d6851c 100644 --- a/lib/runtime_tools/examples/garbage-collection.systemtap +++ b/lib/runtime_tools/examples/garbage-collection.systemtap @@ -18,33 +18,33 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("gc_major-start") +probe process("beam.smp").mark("gc_major-start") { printf("GC major start pid %s need %d words\n", user_string($arg1), $arg2); } -probe process("beam").mark("gc_minor-start") +probe process("beam.smp").mark("gc_minor-start") { printf("GC minor start pid %s need %d words\n", user_string($arg1), $arg2); } -probe process("beam").mark("gc_major-end") +probe process("beam.smp").mark("gc_major-end") { printf("GC major end pid %s reclaimed %d words\n", user_string($arg1), $arg2); } -probe process("beam").mark("gc_minor-start") +probe process("beam.smp").mark("gc_minor-start") { printf("GC minor end pid %s reclaimed %d words\n", user_string($arg1), $arg2); } diff --git a/lib/runtime_tools/examples/memory1.systemtap b/lib/runtime_tools/examples/memory1.systemtap index 04df4d64c4..2fdc5a796c 100644 --- a/lib/runtime_tools/examples/memory1.systemtap +++ b/lib/runtime_tools/examples/memory1.systemtap @@ -18,34 +18,34 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("copy-struct") +probe process("beam.smp").mark("copy-struct") { printf("copy_struct %d bytes\n", $arg1); } -probe process("beam").mark("copy-object") +probe process("beam.smp").mark("copy-object") { printf("copy_object pid %s %d bytes\n", user_string($arg1), $arg2); } -probe process("beam").mark("process-heap_grow") +probe process("beam.smp").mark("process-heap_grow") { printf("proc heap grow pid %s %d -> %d bytes\n", user_string($arg1), $arg2, $arg3); } -probe process("beam").mark("process-heap_shrink") +probe process("beam.smp").mark("process-heap_shrink") { printf("proc heap shrink pid %s %d -> %d bytes\n", user_string($arg1), $arg2, $arg3); diff --git a/lib/runtime_tools/examples/messages.systemtap b/lib/runtime_tools/examples/messages.systemtap index f2ef56a22b..49b7f46d69 100644 --- a/lib/runtime_tools/examples/messages.systemtap +++ b/lib/runtime_tools/examples/messages.systemtap @@ -18,15 +18,15 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ probe begin @@ -38,7 +38,7 @@ probe begin printf("\n"); } -probe process("beam").mark("message-send") +probe process("beam.smp").mark("message-send") { if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { printf("send: %s -> %s: %d words\n", @@ -51,7 +51,7 @@ probe process("beam").mark("message-send") } } -probe process("beam").mark("message-send-remote") +probe process("beam.smp").mark("message-send-remote") { if ($arg5 == 0 && $arg6 == 0 && $arg7 == 0) { printf("send : %s -> %s %s: %d words\n", @@ -64,7 +64,7 @@ probe process("beam").mark("message-send-remote") } } -probe process("beam").mark("message-queued") +probe process("beam.smp").mark("message-queued") { if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { printf("queued: %s: %d words, queue len %d\n", user_string($arg1), $arg2, $arg3); @@ -75,7 +75,7 @@ probe process("beam").mark("message-queued") } } -probe process("beam").mark("message-receive") +probe process("beam.smp").mark("message-receive") { if ($arg4 == 0 && $arg5 == 0 && $arg6 == 0) { printf("receive: %s: %d words, queue len %d\n", diff --git a/lib/runtime_tools/examples/port1.systemtap b/lib/runtime_tools/examples/port1.systemtap index f7ce03a65e..235581b0b1 100644 --- a/lib/runtime_tools/examples/port1.systemtap +++ b/lib/runtime_tools/examples/port1.systemtap @@ -18,15 +18,15 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ probe begin @@ -96,19 +96,19 @@ probe begin driver_map["udp_inet", 62] = "BINDX"; } -probe process("beam").mark("port-open") +probe process("beam.smp").mark("port-open") { printf("port open pid %s port name %s port %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("port-command") +probe process("beam.smp").mark("port-command") { printf("port command pid %s port %s port name %s command type %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); } -probe process("beam").mark("port-control") +probe process("beam.smp").mark("port-control") { cmd = driver_map[user_string($arg3), $arg4]; cmd_str = (cmd == "") ? "unknown" : cmd; @@ -118,36 +118,36 @@ probe process("beam").mark("port-control") /* port-exit is fired as a result of port_close() or exit signal */ -probe process("beam").mark("port-exit") +probe process("beam.smp").mark("port-exit") { printf("port exit pid %s port %s port name %s reason %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); } -probe process("beam").mark("port-connect") +probe process("beam.smp").mark("port-connect") { printf("port connect pid %s port %s port name %s new pid %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); } -probe process("beam").mark("port-busy") +probe process("beam.smp").mark("port-busy") { printf("port busy %s\n", user_string($arg1)); } -probe process("beam").mark("port-not_busy") +probe process("beam.smp").mark("port-not_busy") { printf("port not busy %s\n", user_string($arg1)); } -probe process("beam").mark("aio_pool-add") +probe process("beam.smp").mark("aio_pool-add") { printf("async I/O pool add thread %d queue len %d\n", $arg1, $arg2); } -probe process("beam").mark("aio_pool-get") +probe process("beam.smp").mark("aio_pool-get") { printf("async I/O pool get thread %d queue len %d\n", $arg1, $arg2); } -global driver_map;
\ No newline at end of file +global driver_map; diff --git a/lib/runtime_tools/examples/process-scheduling.systemtap b/lib/runtime_tools/examples/process-scheduling.systemtap index b0b74257b3..231c589f64 100644 --- a/lib/runtime_tools/examples/process-scheduling.systemtap +++ b/lib/runtime_tools/examples/process-scheduling.systemtap @@ -18,28 +18,28 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("process-scheduled") +probe process("beam.smp").mark("process-scheduled") { printf(" Schedule pid %s mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("process-unscheduled") +probe process("beam.smp").mark("process-unscheduled") { printf("Unschedule pid %s\n", user_string($arg1)); } -probe process("beam").mark("process-hibernate") +probe process("beam.smp").mark("process-hibernate") { printf(" Hibernate pid %s resume mfa %s\n", user_string($arg1), user_string($arg2)); diff --git a/lib/runtime_tools/examples/spawn-exit.systemtap b/lib/runtime_tools/examples/spawn-exit.systemtap index 89bca14496..a7b4a0a3ea 100644 --- a/lib/runtime_tools/examples/spawn-exit.systemtap +++ b/lib/runtime_tools/examples/spawn-exit.systemtap @@ -18,34 +18,34 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("process-spawn") +probe process("beam.smp").mark("process-spawn") { printf("pid %s mfa %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("process-exit") +probe process("beam.smp").mark("process-exit") { printf("pid %s reason %s\n", user_string($arg1), user_string($arg2)); } -probe process("beam").mark("process-exit_signal") +probe process("beam.smp").mark("process-exit_signal") { printf("sender %s -> pid %s reason %s\n", user_string($arg1), user_string($arg2), user_string($arg3)); } -probe process("beam").mark("process-exit_signal-remote") +probe process("beam.smp").mark("process-exit_signal-remote") { printf("sender %s -> node %s pid %s reason %s\n", user_string($arg1), user_string($arg2), user_string($arg3), user_string($arg4)); diff --git a/lib/runtime_tools/examples/user-probe-n.systemtap b/lib/runtime_tools/examples/user-probe-n.systemtap index 25f7503283..8a0a89c931 100644 --- a/lib/runtime_tools/examples/user-probe-n.systemtap +++ b/lib/runtime_tools/examples/user-probe-n.systemtap @@ -18,18 +18,19 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("user_trace-n0") + +probe process("beam.smp").mark("user_trace-n0") { printf("probe n0: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", user_string($arg1), @@ -41,7 +42,7 @@ probe process("beam").mark("user_trace-n0") $arg9 == NULL ? "" : user_string($arg9)); } -probe process("beam").mark("user_trace-n1") +probe process("beam.smp").mark("user_trace-n1") { printf("probe n1: %s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", user_string($arg1), diff --git a/lib/runtime_tools/examples/user-probe.systemtap b/lib/runtime_tools/examples/user-probe.systemtap index 1777476e54..ce9dde30f8 100644 --- a/lib/runtime_tools/examples/user-probe.systemtap +++ b/lib/runtime_tools/examples/user-probe.systemtap @@ -18,23 +18,23 @@ * %CopyrightEnd% */ /* - * Note: This file assumes that you're using the non-SMP-enabled Erlang - * virtual machine, "beam". The SMP-enabled VM is called "beam.smp". + * Note: This file assumes that you're using the SMP-enabled Erlang + * virtual machine, "beam.smp". * Note that other variations of the virtual machine also have * different names, e.g. the debug build of the SMP-enabled VM * is "beam.debug.smp". * * To use a different virtual machine, replace each instance of - * "beam" with "beam.smp" or the VM name appropriate to your - * environment. + * "beam.smp" with "beam.debug.smp" or the VM name appropriate + * to your environment. */ -probe process("beam").mark("user_trace-s1") +probe process("beam.smp").mark("user_trace-s1") { printf("%s\n", user_string($arg1)); } -probe process("beam").mark("user_trace-i4s4") +probe process("beam.smp").mark("user_trace-i4s4") { printf("%s %s %d %d %d %d '%s' '%s' '%s' '%s'\n", user_string($arg1), diff --git a/lib/ssh/doc/src/notes.xml b/lib/ssh/doc/src/notes.xml index 177784384e..2e1b946ebb 100644 --- a/lib/ssh/doc/src/notes.xml +++ b/lib/ssh/doc/src/notes.xml @@ -30,6 +30,21 @@ <file>notes.xml</file> </header> +<section><title>Ssh 4.7.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed port leakage if a ssh:daemon call failed.</p> + <p> + Own Id: OTP-15397 Aux Id: ERL-801 </p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.7.2</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -293,6 +308,21 @@ </section> </section> +<section><title>Ssh 4.6.9.3</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p> + Fixed port leakage if a ssh:daemon call failed.</p> + <p> + Own Id: OTP-15397 Aux Id: ERL-801 </p> + </item> + </list> + </section> + +</section> + <section><title>Ssh 4.6.9.2</title> <section><title>Fixed Bugs and Malfunctions</title> diff --git a/lib/ssh/src/ssh.erl b/lib/ssh/src/ssh.erl index 086fa6e5f8..9281bf84a7 100644 --- a/lib/ssh/src/ssh.erl +++ b/lib/ssh/src/ssh.erl @@ -270,25 +270,38 @@ daemon(Host0, Port0, UserOptions0) when 0 =< Port0, Port0 =< 65535, try {Host1, UserOptions} = handle_daemon_args(Host0, UserOptions0), #{} = Options0 = ssh_options:handle_options(server, UserOptions), - - {{Host,Port}, ListenSocket} = - open_listen_socket(Host1, Port0, Options0), - - %% Now Host,Port is what to use for the supervisor to register its name, - %% and ListenSocket is for listening on connections. But it is still owned - %% by self()... - - finalize_start(Host, Port, ?GET_OPT(profile, Options0), - ?PUT_INTERNAL_OPT({lsocket,{ListenSocket,self()}}, Options0), - fun(Opts, Result) -> - {_, Callback, _} = ?GET_OPT(transport, Opts), - receive - {request_control, ListenSocket, ReqPid} -> - ok = Callback:controlling_process(ListenSocket, ReqPid), - ReqPid ! {its_yours,ListenSocket}, - Result - end - end) + {open_listen_socket(Host1, Port0, Options0), Options0} + of + {{{Host,Port}, ListenSocket}, Options1} -> + try + %% Now Host,Port is what to use for the supervisor to register its name, + %% and ListenSocket is for listening on connections. But it is still owned + %% by self()... + finalize_start(Host, Port, ?GET_OPT(profile, Options1), + ?PUT_INTERNAL_OPT({lsocket,{ListenSocket,self()}}, Options1), + fun(Opts, Result) -> + {_, Callback, _} = ?GET_OPT(transport, Opts), + receive + {request_control, ListenSocket, ReqPid} -> + ok = Callback:controlling_process(ListenSocket, ReqPid), + ReqPid ! {its_yours,ListenSocket}, + Result + end + end) + of + {error,Err} -> + close_listen_socket(ListenSocket, Options1), + {error,Err}; + OK -> + OK + catch + error:Error -> + close_listen_socket(ListenSocket, Options1), + error(Error); + exit:Exit -> + close_listen_socket(ListenSocket, Options1), + exit(Exit) + end catch throw:bad_fd -> {error,bad_fd}; @@ -524,6 +537,15 @@ open_listen_socket(_Host0, Port0, Options0) -> {{LHost,LPort}, LSock}. %%%---------------------------------------------------------------- +close_listen_socket(ListenSocket, Options) -> + try + {_, Callback, _} = ?GET_OPT(transport, Options), + Callback:close(ListenSocket) + catch + _C:_E -> ok + end. + +%%%---------------------------------------------------------------- finalize_start(Host, Port, Profile, Options0, F) -> try %% throws error:Error if no usable hostkey is found diff --git a/lib/ssh/src/ssh_sftpd.erl b/lib/ssh/src/ssh_sftpd.erl index 278f6a9780..aa9ba0f9bb 100644 --- a/lib/ssh/src/ssh_sftpd.erl +++ b/lib/ssh/src/ssh_sftpd.erl @@ -508,11 +508,8 @@ close_our_file({_,Fd}, FileMod, FS0) -> FS1. %%% stat: do the stat -stat(Vsn, ReqId, Data, State, F) when Vsn =< 3-> - <<?UINT32(BLen), BPath:BLen/binary>> = Data, - stat(ReqId, unicode:characters_to_list(BPath), State, F); -stat(Vsn, ReqId, Data, State, F) when Vsn >= 4-> - <<?UINT32(BLen), BPath:BLen/binary, ?UINT32(_Flags)>> = Data, +stat(Vsn, ReqId, Data, State, F) -> + <<?UINT32(BLen), BPath:BLen/binary, _/binary>> = Data, stat(ReqId, unicode:characters_to_list(BPath), State, F). fstat(Vsn, ReqId, Data, State) when Vsn =< 3-> diff --git a/lib/ssh/test/ssh_basic_SUITE.erl b/lib/ssh/test/ssh_basic_SUITE.erl index da94b5722f..5de6d52092 100644 --- a/lib/ssh/test/ssh_basic_SUITE.erl +++ b/lib/ssh/test/ssh_basic_SUITE.erl @@ -43,7 +43,9 @@ suite() -> {timetrap,{seconds,40}}]. all() -> - [{group, all_tests}]. + [{group, all_tests}, + daemon_already_started + ]. groups() -> [{all_tests, [parallel], [{group, ssh_renegotiate_SUITE}, @@ -801,6 +803,24 @@ daemon_already_started(Config) when is_list(Config) -> ssh:stop_daemon(Pid). %%-------------------------------------------------------------------- +%%% Test that a failed daemon start does not leave the port open +daemon_error_closes_port(Config) -> + GoodSystemDir = proplists:get_value(data_dir, Config), + Port = ssh_test_lib:inet_port(), + {error,_} = ssh_test_lib:daemon(Port, []), % No system dir + case ssh_test_lib:daemon(Port, [{system_dir, GoodSystemDir}]) of + {error,eaddrinuse} -> + {fail, "Port leakage"}; + {error,Error} -> + ct:log("Strange error: ~p",[Error]), + {fail, "Strange error"}; + {Pid, _Host, Port} -> + %% Ok + ssh:stop_daemon(Pid) + end. + + +%%-------------------------------------------------------------------- %%% check that known_hosts is updated correctly known_hosts(Config) when is_list(Config) -> SystemDir = proplists:get_value(data_dir, Config), diff --git a/lib/ssh/test/ssh_compat_SUITE.erl b/lib/ssh/test/ssh_compat_SUITE.erl index f4eef2dc77..8e82527c6e 100644 --- a/lib/ssh/test/ssh_compat_SUITE.erl +++ b/lib/ssh/test/ssh_compat_SUITE.erl @@ -1126,7 +1126,24 @@ prepare_local_directory(ServerRootDir) -> "chmod 222 unreadable_file", "exit"]. + check_local_directory(ServerRootDir) -> + TimesToTry = 3, % sleep 0.5, 1, 2 and then 4 secs (7.5s in total) + check_local_directory(ServerRootDir, 500, TimesToTry-1). + +check_local_directory(ServerRootDir, SleepTime, N) -> + case do_check_local_directory(ServerRootDir) of + {error,Error} when N>0 -> + %% Could be that the erlang side is faster and the docker's operations + %% are not yet finalized. + %% Sleep for a while and retry a few times: + timer:sleep(SleepTime), + check_local_directory(ServerRootDir, 2*SleepTime, N-1); + Other -> + Other + end. + +do_check_local_directory(ServerRootDir) -> case lists:sort(ok(file:list_dir(ServerRootDir)) -- [".",".."]) of ["ex_tst1","mydir","tst2"] -> {ok,Expect} = file:read_file(filename:join(ServerRootDir,"ex_tst1")), @@ -1161,6 +1178,7 @@ check_local_directory(ServerRootDir) -> {error,{bad_dir_contents,"/"}} end. + call_sftp_in_docker(Config, ServerIP, ServerPort, Cmnds, UserDir) -> {DockerIP,DockerPort} = ip_port(Config), {ok,C} = ssh:connect(DockerIP, DockerPort, diff --git a/lib/ssh/vsn.mk b/lib/ssh/vsn.mk index 3ba1e177be..2890d7fe5b 100644 --- a/lib/ssh/vsn.mk +++ b/lib/ssh/vsn.mk @@ -1,4 +1,4 @@ #-*-makefile-*- ; force emacs to enter makefile-mode -SSH_VSN = 4.7.2 +SSH_VSN = 4.7.3 APP_VSN = "ssh-$(SSH_VSN)" diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml index b4aa8746f9..586452efd4 100644 --- a/lib/ssl/doc/src/ssl.xml +++ b/lib/ssl/doc/src/ssl.xml @@ -83,8 +83,9 @@ <p><c>| {ciphers, ciphers()}</c></p> <p><c>| {user_lookup_fun, {fun(), term()}}, {psk_identity, string()}, {srp_identity, {string(), string()}}</c></p> - <p><c>| {reuse_sessions, boolean()}</c></p> - <p><c>| {reuse_session, fun()} {next_protocols_advertised, [binary()]}</c></p> + <p><c>| {reuse_sessions, boolean() | save()}</c></p> + <p><c>| {reuse_session, fun() | binary()} </c></p> + <p><c>| {next_protocols_advertised, [binary()]}</c></p> <p><c>| {client_preferred_next_protocols, {client | server, [binary()]} | {client | server, [binary()], binary()}}</c></p> <p><c>| {log_alert, boolean()}</c></p> @@ -575,11 +576,23 @@ fun(srp, Username :: string(), UserState :: term()) -> <item><p>In mode <c>verify_none</c> the default behavior is to allow all x509-path validation errors. See also option <c>verify_fun</c>.</p> </item> + + <tag><marker id="client_reuse_session"/><c>{reuse_session, binary()}</c></tag> + <item><p>Reuses a specific session earlier saved with the option + <c>{reuse_sessions, save} since ssl-9.2</c> + </p></item> - <tag><c>{reuse_sessions, boolean()}</c></tag> - <item><p>Specifies if the client is to try to reuse sessions - when possible.</p></item> - + <tag><c>{reuse_sessions, boolean() | save}</c></tag> + <item><p>When <c>save</c> is specified a new connection will be negotiated + and saved for later reuse. The session ID can be fetched with + <seealso marker="#connection_information">connection_information/2</seealso> + and used with the client option <seealso marker="#client_reuse_session">reuse_session</seealso> + The boolean value true specifies that if possible, automatized session reuse will + be performed. If a new session is created, and is unique in regard + to previous stored sessions, it will be saved for possible later reuse. + Value <c>save</c> since ssl-9.2 + </p></item> + <tag><c>{cacerts, [public_key:der_encoded()]}</c></tag> <item><p>The DER-encoded trusted certificates. If this option is supplied it overrides option <c>cacertfile</c>.</p></item> @@ -758,11 +771,14 @@ fun(srp, Username :: string(), UserState :: term()) -> </item> <tag><c>{reuse_sessions, boolean()}</c></tag> - <item><p>Specifies if the server is to agree to reuse sessions - when requested by the clients. See also option <c>reuse_session</c>. + <item><p>The boolean value true specifies that the server will + agree to reuse sessions. Setting it to false will result in an empty + session table, that is no sessions will be reused. + See also option <seealso marker="#server_reuse_session">reuse_session</seealso> </p></item> - <tag><c>{reuse_session, fun(SuggestedSessionId, + <tag><marker id="server_reuse_session"/> + <c>{reuse_session, fun(SuggestedSessionId, PeerCert, Compression, CipherSuite) -> boolean()}</c></tag> <item><p>Enables the TLS/DTLS server to have a local policy for deciding if a session is to be reused or not. diff --git a/lib/ssl/src/dtls_handshake.erl b/lib/ssl/src/dtls_handshake.erl index 3f70eaec8a..1917d51c03 100644 --- a/lib/ssl/src/dtls_handshake.erl +++ b/lib/ssl/src/dtls_handshake.erl @@ -215,8 +215,6 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, dtls_v1:corresponding_tls_version(Version), SslOpts, Session0, ConnectionStates0, Renegotiation) of - #alert{} = Alert -> - Alert; {Session, ConnectionStates, Protocol, ServerHelloExt} -> {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign} catch throw:Alert -> @@ -225,17 +223,16 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites, handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) -> - case ssl_handshake:handle_server_hello_extensions(dtls_record, Random, CipherSuite, - Compression, HelloExt, - dtls_v1:corresponding_tls_version(Version), - SslOpt, ConnectionStates0, Renegotiation) of - #alert{} = Alert -> - Alert; + try ssl_handshake:handle_server_hello_extensions(dtls_record, Random, CipherSuite, + Compression, HelloExt, + dtls_v1:corresponding_tls_version(Version), + SslOpt, ConnectionStates0, Renegotiation) of {ConnectionStates, ProtoExt, Protocol} -> {Version, SessionId, ConnectionStates, ProtoExt, Protocol} + catch throw:Alert -> + Alert end. - %%-------------------------------------------------------------------- enc_handshake(#hello_verify_request{protocol_version = {Major, Minor}, diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 03a1e40bfc..29129d4b6c 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -891,8 +891,6 @@ handle_options(Opts0, Role, Host) -> {list, [{mode, list}]}], Opts0), assert_proplist(Opts), RecordCb = record_cb(Opts), - - ReuseSessionFun = fun(_, _, _, _) -> true end, CaCerts = handle_option(cacerts, Opts, undefined), {Verify, FailIfNoPeerCert, CaCertDefault, VerifyFun, PartialChainHanlder, VerifyClientOnce} = @@ -945,9 +943,8 @@ handle_options(Opts0, Role, Host) -> default_option_role(server, tls_v1:default_signature_algs(Versions), Role)), tls_version(RecordCb:highest_protocol_version(Versions))), - %% Server side option - reuse_session = handle_option(reuse_session, Opts, ReuseSessionFun), - reuse_sessions = handle_option(reuse_sessions, Opts, true), + reuse_sessions = handle_reuse_sessions_option(reuse_sessions, Opts, Role), + reuse_session = handle_reuse_session_option(reuse_session, Opts, Role), secure_renegotiate = handle_option(secure_renegotiate, Opts, true), client_renegotiation = handle_option(client_renegotiation, Opts, default_option_role(server, true, Role), @@ -1138,11 +1135,16 @@ validate_option(srp_identity, {Username, Password}) {unicode:characters_to_binary(Username), unicode:characters_to_binary(Password)}; +validate_option(reuse_session, undefined) -> + undefined; validate_option(reuse_session, Value) when is_function(Value) -> Value; +validate_option(reuse_session, Value) when is_binary(Value) -> + Value; validate_option(reuse_sessions, Value) when is_boolean(Value) -> Value; - +validate_option(reuse_sessions, save = Value) -> + Value; validate_option(secure_renegotiate, Value) when is_boolean(Value) -> Value; validate_option(client_renegotiation, Value) when is_boolean(Value) -> @@ -1265,6 +1267,26 @@ handle_hashsigns_option(_, Version) when Version >= {3, 3} -> handle_hashsigns_option(_, _Version) -> undefined. +handle_reuse_sessions_option(Key, Opts, client) -> + Value = proplists:get_value(Key, Opts, true), + validate_option(Key, Value), + Value; +handle_reuse_sessions_option(Key, Opts0, server) -> + Opts = proplists:delete({Key, save}, Opts0), + Value = proplists:get_value(Key, Opts, true), + validate_option(Key, Value), + Value. + +handle_reuse_session_option(Key, Opts, client) -> + Value = proplists:get_value(Key, Opts, undefined), + validate_option(Key, Value), + Value; +handle_reuse_session_option(Key, Opts, server) -> + ReuseSessionFun = fun(_, _, _, _) -> true end, + Value = proplists:get_value(Key, Opts, ReuseSessionFun), + validate_option(Key, Value), + Value. + validate_options([]) -> []; validate_options([{Opt, Value} | Tail]) -> diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 41d853977e..7d7da2dcec 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -1044,7 +1044,7 @@ cipher(internal, #finished{verify_data = Data} = Finished, get_current_prf(ConnectionStates0, read), MasterSecret, Handshake0) of verified -> - Session = register_session(Role, host_id(Role, Host, SslOpts), Port, Session0), + Session = handle_session(Role, SslOpts, Host, Port, Session0), cipher_role(Role, Data, Session, State#state{expecting_finished = false}, Connection); #alert{} = Alert -> @@ -1193,7 +1193,7 @@ handle_call({shutdown, read_write = How}, From, StateName, ok -> {next_state, StateName, State#state{terminated = true}, [{reply, From, ok}]}; Error -> - {stop, StateName, State#state{terminated = true}, [{reply, From, Error}]} + {stop_and_reply, {shutdown, normal}, {reply, From, Error}, State#state{terminated = true}} end catch throw:Return -> @@ -1206,7 +1206,7 @@ handle_call({shutdown, How0}, From, StateName, ok -> {next_state, StateName, State, [{reply, From, ok}]}; Error -> - {stop, StateName, State, [{reply, From, Error}]} + {stop_and_reply, {shutdown, normal}, {reply, From, Error}, State} end; handle_call({recv, _N, _Timeout}, From, _, #state{socket_options = @@ -2406,15 +2406,35 @@ session_handle_params(#server_ecdh_params{curve = ECCurve}, Session) -> session_handle_params(_, Session) -> Session. -register_session(client, Host, Port, #session{is_resumable = new} = Session0) -> +handle_session(Role = server, #ssl_options{reuse_sessions = true} = SslOpts, + Host, Port, Session0) -> + register_session(Role, host_id(Role, Host, SslOpts), Port, Session0, true); +handle_session(Role = client, #ssl_options{verify = verify_peer, + reuse_sessions = Reuse} = SslOpts, + Host, Port, Session0) when Reuse =/= false -> + register_session(Role, host_id(Role, Host, SslOpts), Port, Session0, reg_type(Reuse)); +handle_session(server, _, Host, Port, Session) -> + %% Remove "session of type new" entry from session DB + ssl_manager:invalidate_session(Host, Port, Session), + Session; +handle_session(client, _,_,_, Session) -> + %% In client case there is no entry yet, so nothing to remove + Session. + +reg_type(save) -> + true; +reg_type(true) -> + unique. + +register_session(client, Host, Port, #session{is_resumable = new} = Session0, Save) -> Session = Session0#session{is_resumable = true}, - ssl_manager:register_session(Host, Port, Session), + ssl_manager:register_session(Host, Port, Session, Save), Session; -register_session(server, _, Port, #session{is_resumable = new} = Session0) -> +register_session(server, _, Port, #session{is_resumable = new} = Session0, _) -> Session = Session0#session{is_resumable = true}, ssl_manager:register_session(Port, Session), Session; -register_session(_, _, _, Session) -> +register_session(_, _, _, Session, _) -> Session. %% Already registered host_id(client, _Host, #ssl_options{server_name_indication = Hostname}) when is_list(Hostname) -> diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl index 14df1d2e02..3da42eb8ac 100644 --- a/lib/ssl/src/ssl_handshake.erl +++ b/lib/ssl/src/ssl_handshake.erl @@ -610,7 +610,7 @@ encode_hello_extensions([#ec_point_formats{ec_point_format_list = ECPointFormats ?UINT16(Len), ?BYTE(ListLen), ECPointFormatList/binary, Acc/binary>>); encode_hello_extensions([#srp{username = UserName} | Rest], Acc) -> SRPLen = byte_size(UserName), - Len = SRPLen + 2, + Len = SRPLen + 1, encode_hello_extensions(Rest, <<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), UserName/binary, Acc/binary>>); encode_hello_extensions([#hash_sign_algos{hash_sign_algos = HashSignAlgos} | Rest], Acc) -> @@ -972,34 +972,30 @@ handle_client_hello_extensions(RecordCB, Random, ClientCipherSuites, #session{cipher_suite = NegotiatedCipherSuite, compression_method = Compression} = Session0, ConnectionStates0, Renegotiation) -> - Session = handle_srp_extension(SRP, Session0), - ConnectionStates = handle_renegotiation_extension(server, RecordCB, Version, Info, - Random, NegotiatedCipherSuite, + Session = handle_srp_extension(SRP, Session0), + ConnectionStates = handle_renegotiation_extension(server, RecordCB, Version, Info, + Random, NegotiatedCipherSuite, ClientCipherSuites, Compression, - ConnectionStates0, Renegotiation, SecureRenegotation), - - ServerHelloExtensions = #hello_extensions{ - renegotiation_info = renegotiation_info(RecordCB, server, - ConnectionStates, Renegotiation), - ec_point_formats = server_ecc_extension(Version, ECCFormat) - }, - + ConnectionStates0, Renegotiation, SecureRenegotation), + + ServerHelloExtensions = #hello_extensions{ + renegotiation_info = renegotiation_info(RecordCB, server, + ConnectionStates, Renegotiation), + ec_point_formats = server_ecc_extension(Version, ECCFormat) + }, + %% If we receive an ALPN extension and have ALPN configured for this connection, %% we handle it. Otherwise we check for the NPN extension. if ALPN =/= undefined, ALPNPreferredProtocols =/= undefined -> - case handle_alpn_extension(ALPNPreferredProtocols, decode_alpn(ALPN)) of - #alert{} = Alert -> - Alert; - Protocol -> - {Session, ConnectionStates, Protocol, - ServerHelloExtensions#hello_extensions{alpn=encode_alpn([Protocol], Renegotiation)}} - end; + Protocol = handle_alpn_extension(ALPNPreferredProtocols, decode_alpn(ALPN)), + {Session, ConnectionStates, Protocol, + ServerHelloExtensions#hello_extensions{alpn=encode_alpn([Protocol], Renegotiation)}}; true -> - ProtocolsToAdvertise = handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, Opts), + ProtocolsToAdvertise = handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, Opts), {Session, ConnectionStates, undefined, - ServerHelloExtensions#hello_extensions{next_protocol_negotiation= - encode_protocols_advertised_on_server(ProtocolsToAdvertise)}} + ServerHelloExtensions#hello_extensions{next_protocol_negotiation= + encode_protocols_advertised_on_server(ProtocolsToAdvertise)}} end. handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, @@ -1022,12 +1018,8 @@ handle_server_hello_extensions(RecordCB, Random, CipherSuite, Compression, [Protocol] when not Renegotiation -> {ConnectionStates, alpn, Protocol}; undefined -> - case handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation) of - #alert{} = Alert -> - Alert; - Protocol -> - {ConnectionStates, npn, Protocol} - end; + Protocol = handle_next_protocol(NextProtocolNegotiation, NextProtoSelector, Renegotiation), + {ConnectionStates, npn, Protocol}; {error, Reason} -> ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason); [] -> @@ -1941,7 +1933,7 @@ dec_hello_extensions(<<?UINT16(?RENEGOTIATION_EXT), ?UINT16(Len), Info:Len/binar RenegotiateInfo}}); dec_hello_extensions(<<?UINT16(?SRP_EXT), ?UINT16(Len), ?BYTE(SRPLen), SRP:SRPLen/binary, Rest/binary>>, Acc) - when Len == SRPLen + 2 -> + when Len == SRPLen + 1 -> dec_hello_extensions(Rest, Acc#hello_extensions{srp = #srp{username = SRP}}); dec_hello_extensions(<<?UINT16(?SIGNATURE_ALGORITHMS_EXT), ?UINT16(Len), @@ -2189,30 +2181,26 @@ filter_unavailable_ecc_suites(_, Suites) -> handle_renegotiation_extension(Role, RecordCB, Version, Info, Random, NegotiatedCipherSuite, ClientCipherSuites, Compression, ConnectionStates0, Renegotiation, SecureRenegotation) -> - case handle_renegotiation_info(RecordCB, Role, Info, ConnectionStates0, - Renegotiation, SecureRenegotation, - ClientCipherSuites) of - {ok, ConnectionStates} -> - hello_pending_connection_states(RecordCB, Role, - Version, - NegotiatedCipherSuite, - Random, - Compression, - ConnectionStates); - #alert{} = Alert -> - throw(Alert) - end. + {ok, ConnectionStates} = handle_renegotiation_info(RecordCB, Role, Info, ConnectionStates0, + Renegotiation, SecureRenegotation, + ClientCipherSuites), + hello_pending_connection_states(RecordCB, Role, + Version, + NegotiatedCipherSuite, + Random, + Compression, + ConnectionStates). %% Receive protocols, choose one from the list, return it. handle_alpn_extension(_, {error, Reason}) -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason); + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, Reason)); handle_alpn_extension([], _) -> - ?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL); + throw(?ALERT_REC(?FATAL, ?NO_APPLICATION_PROTOCOL)); handle_alpn_extension([ServerProtocol|Tail], ClientProtocols) -> - case lists:member(ServerProtocol, ClientProtocols) of - true -> ServerProtocol; - false -> handle_alpn_extension(Tail, ClientProtocols) - end. + case lists:member(ServerProtocol, ClientProtocols) of + true -> ServerProtocol; + false -> handle_alpn_extension(Tail, ClientProtocols) + end. handle_next_protocol(undefined, _NextProtocolSelector, _Renegotiating) -> @@ -2225,14 +2213,14 @@ handle_next_protocol(#next_protocol_negotiation{} = NextProtocols, true -> select_next_protocol(decode_next_protocols(NextProtocols), NextProtocolSelector); false -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, unexpected_next_protocol_extension) + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, unexpected_next_protocol_extension)) end. handle_next_protocol_extension(NextProtocolNegotiation, Renegotiation, SslOpts)-> case handle_next_protocol_on_server(NextProtocolNegotiation, Renegotiation, SslOpts) of #alert{} = Alert -> - Alert; + throw(Alert); ProtocolsToAdvertise -> ProtocolsToAdvertise end. @@ -2428,14 +2416,14 @@ handle_renegotiation_info(_RecordCB, client, #renegotiation_info{renegotiated_co true -> {ok, ConnectionStates}; false -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, client_renegotiation) + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, client_renegotiation)) end; handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_connection = ClientVerify}, ConnectionStates, true, _, CipherSuites) -> case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of true -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv}); + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv})); false -> ConnectionState = ssl_record:current_connection_state(ConnectionStates, read), Data = maps:get(client_verify_data, ConnectionState), @@ -2443,7 +2431,7 @@ handle_renegotiation_info(_RecordCB, server, #renegotiation_info{renegotiated_co true -> {ok, ConnectionStates}; false -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, server_renegotiation) + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, server_renegotiation)) end end; @@ -2453,7 +2441,7 @@ handle_renegotiation_info(RecordCB, client, undefined, ConnectionStates, true, S handle_renegotiation_info(RecordCB, server, undefined, ConnectionStates, true, SecureRenegotation, CipherSuites) -> case is_member(?TLS_EMPTY_RENEGOTIATION_INFO_SCSV, CipherSuites) of true -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv}); + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, {server_renegotiation, empty_renegotiation_info_scsv})); false -> handle_renegotiation_info(RecordCB, ConnectionStates, SecureRenegotation) end. @@ -2462,9 +2450,9 @@ handle_renegotiation_info(_RecordCB, ConnectionStates, SecureRenegotation) -> ConnectionState = ssl_record:current_connection_state(ConnectionStates, read), case {SecureRenegotation, maps:get(secure_renegotiation, ConnectionState)} of {_, true} -> - ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, already_secure); + throw(?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE, already_secure)); {true, false} -> - ?ALERT_REC(?FATAL, ?NO_RENEGOTIATION); + throw(?ALERT_REC(?FATAL, ?NO_RENEGOTIATION)); {false, false} -> {ok, ConnectionStates} end. diff --git a/lib/ssl/src/ssl_internal.hrl b/lib/ssl/src/ssl_internal.hrl index 63e751440a..91623db79e 100644 --- a/lib/ssl/src/ssl_internal.hrl +++ b/lib/ssl/src/ssl_internal.hrl @@ -111,10 +111,10 @@ %% Local policy for the server if it want's to reuse the session %% or not. Defaluts to allways returning true. %% fun(SessionId, PeerCert, Compression, CipherSuite) -> boolean() - reuse_session, + reuse_session :: fun() | binary() | undefined, %% Server side is a fun() %% If false sessions will never be reused, if true they %% will be reused if possible. - reuse_sessions :: boolean(), + reuse_sessions :: boolean() | save, %% Only client side can use value save renegotiate_at, secure_renegotiate, client_renegotiation, @@ -148,6 +148,8 @@ max_handshake_size :: integer(), handshake, customize_hostname_check + %% , + %% save_session :: boolean() }). -record(socket_options, diff --git a/lib/ssl/src/ssl_manager.erl b/lib/ssl/src/ssl_manager.erl index 4b735b2400..dcd26635c0 100644 --- a/lib/ssl/src/ssl_manager.erl +++ b/lib/ssl/src/ssl_manager.erl @@ -30,7 +30,7 @@ connection_init/3, cache_pem_file/2, lookup_trusted_cert/4, new_session_id/1, clean_cert_db/2, - register_session/2, register_session/3, invalidate_session/2, + register_session/2, register_session/4, invalidate_session/2, insert_crls/2, insert_crls/3, delete_crls/1, delete_crls/2, invalidate_session/3, name/1]). @@ -170,9 +170,11 @@ clean_cert_db(Ref, File) -> %% %% Description: Make the session available for reuse. %%-------------------------------------------------------------------- --spec register_session(host(), inet:port_number(), #session{}) -> ok. -register_session(Host, Port, Session) -> - cast({register_session, Host, Port, Session}). +-spec register_session(host(), inet:port_number(), #session{}, unique | true) -> ok. +register_session(Host, Port, Session, true) -> + call({register_session, Host, Port, Session}); +register_session(Host, Port, Session, unique = Save) -> + cast({register_session, Host, Port, Session, Save}). -spec register_session(inet:port_number(), #session{}) -> ok. register_session(Port, Session) -> @@ -301,7 +303,10 @@ handle_call({{new_session_id, Port}, _}, _, #state{session_cache_cb = CacheCb, session_cache_server = Cache} = State) -> Id = new_id(Port, ?GEN_UNIQUE_ID_MAX_TRIES, Cache, CacheCb), - {reply, Id, State}. + {reply, Id, State}; +handle_call({{register_session, Host, Port, Session},_}, _, State0) -> + State = client_register_session(Host, Port, Session, State0), + {reply, ok, State}. %%-------------------------------------------------------------------- -spec handle_cast(msg(), #state{}) -> {noreply, #state{}}. @@ -311,8 +316,12 @@ handle_call({{new_session_id, Port}, _}, %% %% Description: Handling cast messages %%-------------------------------------------------------------------- -handle_cast({register_session, Host, Port, Session}, State0) -> - State = ssl_client_register_session(Host, Port, Session, State0), +handle_cast({register_session, Host, Port, Session, unique}, State0) -> + State = client_register_unique_session(Host, Port, Session, State0), + {noreply, State}; + +handle_cast({register_session, Host, Port, Session, true}, State0) -> + State = client_register_session(Host, Port, Session, State0), {noreply, State}; handle_cast({register_session, Port, Session}, State0) -> @@ -540,10 +549,10 @@ clean_cert_db(Ref, CertDb, RefDb, FileMapDb, File) -> ok end. -ssl_client_register_session(Host, Port, Session, #state{session_cache_client = Cache, - session_cache_cb = CacheCb, - session_cache_client_max = Max, - session_client_invalidator = Pid0} = State) -> +client_register_unique_session(Host, Port, Session, #state{session_cache_client = Cache, + session_cache_cb = CacheCb, + session_cache_client_max = Max, + session_client_invalidator = Pid0} = State) -> TimeStamp = erlang:monotonic_time(), NewSession = Session#session{time_stamp = TimeStamp}, @@ -557,6 +566,17 @@ ssl_client_register_session(Host, Port, Session, #state{session_cache_client = C register_unique_session(Sessions, NewSession, {Host, Port}, State) end. +client_register_session(Host, Port, Session, #state{session_cache_client = Cache, + session_cache_cb = CacheCb, + session_cache_client_max = Max, + session_client_invalidator = Pid0} = State) -> + TimeStamp = erlang:monotonic_time(), + NewSession = Session#session{time_stamp = TimeStamp}, + Pid = do_register_session({{Host, Port}, + NewSession#session.session_id}, + NewSession, Max, Pid0, Cache, CacheCb), + State#state{session_client_invalidator = Pid}. + server_register_session(Port, Session, #state{session_cache_server_max = Max, session_cache_server = Cache, session_cache_cb = CacheCb, diff --git a/lib/ssl/src/ssl_session.erl b/lib/ssl/src/ssl_session.erl index c9607489e9..a9759c9b43 100644 --- a/lib/ssl/src/ssl_session.erl +++ b/lib/ssl/src/ssl_session.erl @@ -53,6 +53,13 @@ is_new(_ClientSuggestion, _ServerDecision) -> %% Description: Should be called by the client side to get an id %% for the client hello message. %%-------------------------------------------------------------------- +client_id({Host, Port, #ssl_options{reuse_session = SessionId}}, Cache, CacheCb, _) when is_binary(SessionId)-> + case CacheCb:lookup(Cache, {{Host, Port}, SessionId}) of + undefined -> + <<>>; + #session{} -> + SessionId + end; client_id(ClientInfo, Cache, CacheCb, OwnCert) -> case select_session(ClientInfo, Cache, CacheCb, OwnCert) of no_session -> @@ -91,7 +98,8 @@ server_id(Port, SuggestedId, Options, Cert, Cache, CacheCb) -> %%-------------------------------------------------------------------- %%% Internal functions %%-------------------------------------------------------------------- -select_session({_, _, #ssl_options{reuse_sessions=false}}, _Cache, _CacheCb, _OwnCert) -> +select_session({_, _, #ssl_options{reuse_sessions = Reuse}}, _Cache, _CacheCb, _OwnCert) when Reuse =/= true -> + %% If reuse_sessions == true | save a new session should be created no_session; select_session({HostIP, Port, SslOpts}, Cache, CacheCb, OwnCert) -> Sessions = CacheCb:select_session(Cache, {HostIP, Port}), @@ -132,7 +140,7 @@ is_resumable(SuggestedSessionId, Port, #ssl_options{reuse_session = ReuseFun} = false -> {false, undefined} end; undefined -> - {false, undefined} + {false, undefined} end. resumable(new) -> diff --git a/lib/ssl/src/tls_connection.erl b/lib/ssl/src/tls_connection.erl index 9edf48fdef..8b24151d9f 100644 --- a/lib/ssl/src/tls_connection.erl +++ b/lib/ssl/src/tls_connection.erl @@ -893,6 +893,12 @@ handle_alerts([], Result) -> Result; handle_alerts(_, {stop, _, _} = Stop) -> Stop; +handle_alerts([#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} | _Alerts], + {next_state, connection = StateName, #state{user_data_buffer = Buffer, + protocol_buffers = #protocol_buffers{tls_cipher_texts = CTs}} = + State}) when (Buffer =/= <<>>) orelse + (CTs =/= []) -> + {next_state, StateName, State#state{terminated = true}}; handle_alerts([Alert | Alerts], {next_state, StateName, State}) -> handle_alerts(Alerts, ssl_connection:handle_alert(Alert, StateName, State)); handle_alerts([Alert | Alerts], {next_state, StateName, State, _Actions}) -> diff --git a/lib/ssl/src/tls_handshake.erl b/lib/ssl/src/tls_handshake.erl index 19a5eb0348..65217ad68e 100644 --- a/lib/ssl/src/tls_handshake.erl +++ b/lib/ssl/src/tls_handshake.erl @@ -223,8 +223,6 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites, HelloExt, Version, SslOpts, Session0, ConnectionStates0, Renegotiation) of - #alert{} = Alert -> - Alert; {Session, ConnectionStates, Protocol, ServerHelloExt} -> {Version, {Type, Session}, ConnectionStates, Protocol, ServerHelloExt, HashSign} @@ -235,14 +233,14 @@ handle_client_hello_extensions(Version, Type, Random, CipherSuites, handle_server_hello_extensions(Version, SessionId, Random, CipherSuite, Compression, HelloExt, SslOpt, ConnectionStates0, Renegotiation) -> - case ssl_handshake:handle_server_hello_extensions(tls_record, Random, CipherSuite, + try ssl_handshake:handle_server_hello_extensions(tls_record, Random, CipherSuite, Compression, HelloExt, Version, SslOpt, ConnectionStates0, - Renegotiation) of - #alert{} = Alert -> - Alert; + Renegotiation) of {ConnectionStates, ProtoExt, Protocol} -> {Version, SessionId, ConnectionStates, ProtoExt, Protocol} + catch throw:Alert -> + Alert end. %%-------------------------------------------------------------------- enc_handshake(#hello_request{}, _Version) -> diff --git a/lib/ssl/test/make_certs.erl b/lib/ssl/test/make_certs.erl index 8fe7c54549..7f3371da9a 100644 --- a/lib/ssl/test/make_certs.erl +++ b/lib/ssl/test/make_certs.erl @@ -189,6 +189,18 @@ gencrl(Root, CA, C, CrlHours) -> Env = [{"ROOTDIR", filename:absname(Root)}], cmd(Cmd, Env). +%% This function sets the number of seconds until the next CRL is due. +gencrl_sec(Root, CA, C, CrlSecs) -> + CACnfFile = filename:join([Root, CA, "ca.cnf"]), + CACRLFile = filename:join([Root, CA, "crl.pem"]), + Cmd = [C#config.openssl_cmd, " ca" + " -gencrl ", + " -crlsec ", integer_to_list(CrlSecs), + " -out ", CACRLFile, + " -config ", CACnfFile], + Env = [{"ROOTDIR", filename:absname(Root)}], + cmd(Cmd, Env). + can_generate_expired_crls(C) -> %% OpenSSL can generate CRLs with an expiration date in the past, %% if we pass a negative number for -crlhours. However, LibreSSL diff --git a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl index 04c4b257d9..7f7c3da5ab 100644 --- a/lib/ssl/test/ssl_alpn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_alpn_handshake_SUITE.erl @@ -262,52 +262,12 @@ client_renegotiate(Config) when is_list(Config) -> %-------------------------------------------------------------------------------- session_reused(Config) when is_list(Config)-> - ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = [{alpn_advertised_protocols, [<<"http/1.0">>]}] ++ ClientOpts0, ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), ServerOpts = [{alpn_preferred_protocols, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ 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, session_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, {ssl_test_lib, no_result_msg, []}}, - {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} -> - ok; - {Client1, Other} -> - ct:fail(Other) - end, - - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ssl_test_lib:close(Client1). - + ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config). %-------------------------------------------------------------------------------- alpn_not_supported_client(Config) when is_list(Config) -> diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 90fcde609f..3778530a47 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -654,8 +654,8 @@ new_options_in_accept(Config) when is_list(Config) -> handshake_continue() -> [{doc, "Test API function ssl:handshake_continue/3"}]. handshake_continue(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), + 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}, @@ -714,7 +714,7 @@ hello_client_cancel(Config) when is_list(Config) -> 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_verification_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}, @@ -756,8 +756,8 @@ prf(Config) when is_list(Config) -> 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_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, 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}, @@ -1446,8 +1446,8 @@ cipher_suites_mix() -> cipher_suites_mix(Config) when is_list(Config) -> CipherSuites = [{dhe_rsa,aes_128_cbc,sha256,sha256}, {dhe_rsa,aes_128_cbc,sha}], - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, 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), @@ -2358,8 +2358,8 @@ 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_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, 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) -> @@ -2374,27 +2374,28 @@ invalid_options(Config) when is_list(Config) -> {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' }], + 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 = @@ -2687,175 +2688,69 @@ ciphers_ecdh_rsa_signed_certs_openssl_names(Config) when is_list(Config) -> reuse_session() -> [{doc,"Test reuse of sessions (short handshake)"}]. reuse_session(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, 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, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, + ClientOpts = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_opts, Config), - %% 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} -> - ok; - {Client1, Other} -> - ct:log("Expected: ~p, Unexpected: ~p~n", - [SessionInfo, Other]), - ct:fail(session_not_reused) - end, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - - Client2 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, [{reuse_sessions, false} - | ClientOpts]}]), - receive - {Client2, SessionInfo} -> - ct:fail( - session_reused_when_session_reuse_disabled_by_client); - {Client2, _} -> - ok - end, - - ssl_test_lib:close(Server), - - Server1 = - ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, - {from, self()}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {options, [{reuse_sessions, false} | ServerOpts]}]), - - Port1 = ssl_test_lib:inet_port(Server1), - Client3 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port1}, {host, Hostname}, - {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, ClientOpts}]), - - SessionInfo1 = - receive - {Server1, Info1} -> - Info1 - end, - - Server1 ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, - - %% Make sure session is registered - ct:sleep(?SLEEP), - - Client4 = - ssl_test_lib:start_client([{node, ClientNode}, - {port, Port1}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, - {from, self()}, {options, ClientOpts}]), - - receive - {Client4, SessionInfo1} -> - ct:fail( - session_reused_when_session_reuse_disabled_by_server); - {Client4, _Other} -> - ct:log("OTHER: ~p ~n", [_Other]), - ok - end, - - ssl_test_lib:close(Server1), - ssl_test_lib:close(Client0), - ssl_test_lib:close(Client1), - ssl_test_lib:close(Client2), - ssl_test_lib:close(Client3), - ssl_test_lib:close(Client4). - + 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_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, 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}, + + Server0 = + 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, - - Server ! {listen, {mfa, {ssl_test_lib, no_result, []}}}, + {mfa, {ssl_test_lib, no_result, []}}, + {tcp_options, [{active, false}]}, + {options, ServerOpts}]), + Port0 = ssl_test_lib:inet_port(Server0), - %% 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}]), + 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, SessionInfo} -> - ok; - {Client1, Other} -> - ct:log("Expected: ~p, Unexpected: ~p~n", - [SessionInfo, Other]), - ct:fail(session_not_reused) + {Client1, SID} -> + ok + after ?SLEEP -> + ct:fail(session_not_reused) end, - Server ! listen, - + Server0 ! listen, + %% Make sure session is unregistered due to expiration - ct:sleep((?EXPIRE+1)), - [{session_id, Id} |_] = SessionInfo, + ct:sleep((?EXPIRE*2)), - make_sure_expired(Hostname, Port, Id), + make_sure_expired(Hostname, Port0, SID), Client2 = ssl_test_lib:start_client([{node, ClientNode}, - {port, Port}, {host, Hostname}, - {mfa, {ssl_test_lib, session_info_result, []}}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, {from, self()}, {options, ClientOpts}]), receive - {Client2, SessionInfo} -> + {Client2, SID} -> ct:fail(session_reused_when_session_expired); {Client2, _} -> ok end, process_flag(trap_exit, false), - ssl_test_lib:close(Server), + ssl_test_lib:close(Server0), ssl_test_lib:close(Client0), ssl_test_lib:close(Client1), ssl_test_lib:close(Client2). @@ -2864,16 +2759,16 @@ make_sure_expired(Host, Port, Id) -> {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), [_, _,_, _, Prop] = StatusInfo, State = ssl_test_lib:state(Prop), - Cache = element(2, State), + ClientCache = element(2, State), - case ssl_session_cache:lookup(Cache, {{Host, Port}, Id}) of + case ssl_session_cache:lookup(ClientCache, {{Host, Port}, Id}) of undefined -> - ok; + ok; #session{is_resumable = false} -> - ok; + ok; _ -> ct:sleep(?SLEEP), - make_sure_expired(Host, Port, Id) + make_sure_expired(Host, Port, Id) end. %%-------------------------------------------------------------------- @@ -4486,8 +4381,8 @@ tcp_send_recv_result(Socket) -> ok. basic_verify_test_no_close(Config) -> - ClientOpts = ssl_test_lib:ssl_options(client_verification_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_verification_opts, 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), @@ -4962,16 +4857,16 @@ run_suites(Ciphers, Config, Type) -> {ClientOpts, ServerOpts} = case Type of rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_verification_opts, Config)]}; + ssl_test_lib:ssl_options(server_rsa_opts, Config)]}; dsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_dsa_verify_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_dsa_opts, Config)]}; anonymous -> %% No certs in opts! - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options([], Config)]}; psk -> @@ -5001,38 +4896,38 @@ run_suites(Ciphers, Config, Type) -> {ssl_test_lib:ssl_options(client_srp_dsa, Config), ssl_test_lib:ssl_options(server_srp_dsa, Config)}; ecdsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_ecdsa_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]}; ecdh_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_ecdh_rsa_opts, Config), ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)}; rc4_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_verification_opts, Config)]}; + ssl_test_lib:ssl_options(server_rsa_verify_opts, Config)]}; rc4_ecdh_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_ecdh_rsa_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdh_rsa_opts, Config)]}; rc4_ecdsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]}; des_dhe_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_verification_opts, Config)]}; des_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_verification_opts, Config)]}; + ssl_test_lib:ssl_options(server_rsa_verify_opts, Config)]}; chacha_rsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), [{ciphers, Ciphers} | - ssl_test_lib:ssl_options(server_verification_opts, Config)]}; + ssl_test_lib:ssl_options(server_rsa_verify_opts, Config)]}; chacha_ecdsa -> - {ssl_test_lib:ssl_options(client_verification_opts, Config), + {ssl_test_lib:ssl_options(client_ecdsa_opts, Config), [{ciphers, Ciphers} | ssl_test_lib:ssl_options(server_ecdsa_opts, Config)]} end, diff --git a/lib/ssl/test/ssl_crl_SUITE.erl b/lib/ssl/test/ssl_crl_SUITE.erl index 23c5eaf84d..c61039b5da 100644 --- a/lib/ssl/test/ssl_crl_SUITE.erl +++ b/lib/ssl/test/ssl_crl_SUITE.erl @@ -383,8 +383,11 @@ crl_hash_dir_expired(Config) when is_list(Config) -> {verify, verify_peer}], {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), - %% First make a CRL that expired yesterday. - make_certs:gencrl(PrivDir, CA, CertsConfig, -24), + %% First make a CRL that will expire in one second. + make_certs:gencrl_sec(PrivDir, CA, CertsConfig, 1), + %% Sleep until the next CRL is due + ct:sleep({seconds, 1}), + CrlDir = filename:join(PrivDir, "crls"), populate_crl_hash_dir(PrivDir, CrlDir, [{CA, "1627b4b0"}], diff --git a/lib/ssl/test/ssl_handshake_SUITE.erl b/lib/ssl/test/ssl_handshake_SUITE.erl index b8b9989d30..1fa6029963 100644 --- a/lib/ssl/test/ssl_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_handshake_SUITE.erl @@ -25,6 +25,7 @@ -compile(export_all). -include_lib("common_test/include/ct.hrl"). +-include("ssl_handshake.hrl"). -include("ssl_internal.hrl"). -include("tls_handshake.hrl"). -include_lib("public_key/include/public_key.hrl"). @@ -41,7 +42,8 @@ all() -> [decode_hello_handshake, decode_empty_server_sni_correctly, select_proper_tls_1_2_rsa_default_hashsign, ignore_hassign_extension_pre_tls_1_2, - unorded_chain]. + unorded_chain, + encode_decode_srp]. %%-------------------------------------------------------------------- init_per_suite(Config) -> @@ -192,6 +194,31 @@ unorded_chain(Config) when is_list(Config) -> {ok, _, OrderedChain} = ssl_certificate:certificate_chain(PeerCert, ets:new(foo, []), ExtractedCerts, UnordedChain). +encode_decode_srp(_Config) -> + Exts = #hello_extensions{ + srp = #srp{username = <<"foo">>}, + sni = #sni{hostname = "bar"}, + renegotiation_info = undefined, + signature_algs = undefined, + alpn = undefined, + next_protocol_negotiation = undefined, + ec_point_formats = undefined, + elliptic_curves = undefined + }, + EncodedExts = <<0,20, % Length + 0,0, % SNI extension + 0,8, % Length + 0,6, % ServerNameLength + 0, % NameType (host_name) + 0,3, % HostNameLength + 98,97,114, % hostname = "bar" + 0,12, % SRP extension + 0,4, % Length + 3, % srp_I length + 102,111,111>>, % username = "foo" + EncodedExts = ssl_handshake:encode_hello_extensions(Exts), + Exts = ssl_handshake:decode_hello_extensions({client, EncodedExts}). + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ diff --git a/lib/ssl/test/ssl_npn_handshake_SUITE.erl b/lib/ssl/test/ssl_npn_handshake_SUITE.erl index 1c7d6b5f9f..878e983bb9 100644 --- a/lib/ssl/test/ssl_npn_handshake_SUITE.erl +++ b/lib/ssl/test/ssl_npn_handshake_SUITE.erl @@ -64,13 +64,12 @@ next_protocol_not_supported() -> npn_not_supported_server ]. -init_per_suite(Config) -> +init_per_suite(Config0) -> catch crypto:stop(), try crypto:start() of ok -> ssl_test_lib:clean_start(), - {ok, _} = make_certs:all(proplists:get_value(data_dir, Config), - proplists:get_value(priv_dir, Config)), + Config = ssl_test_lib:make_rsa_cert(Config0), ssl_test_lib:cert_options(Config) catch _:_ -> {skip, "Crypto did not start"} @@ -196,10 +195,10 @@ client_negotiate_server_does_not_support(Config) when is_list(Config) -> renegotiate_from_client_after_npn_handshake(Config) when is_list(Config) -> Data = "hello world", - ClientOpts0 = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = [{client_preferred_next_protocols, {client, [<<"http/1.0">>], <<"http/1.1">>}}] ++ ClientOpts0, - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), ServerOpts = [{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ ServerOpts0, ExpectedProtocol = {ok, <<"http/1.0">>}, @@ -221,7 +220,7 @@ renegotiate_from_client_after_npn_handshake(Config) when is_list(Config) -> %-------------------------------------------------------------------------------- npn_not_supported_client(Config) when is_list(Config) -> - ClientOpts0 = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), PrefProtocols = {client_preferred_next_protocols, {client, [<<"http/1.0">>], <<"http/1.1">>}}, ClientOpts = [PrefProtocols] ++ ClientOpts0, @@ -236,7 +235,7 @@ npn_not_supported_client(Config) when is_list(Config) -> %-------------------------------------------------------------------------------- npn_not_supported_server(Config) when is_list(Config)-> - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), AdvProtocols = {next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}, ServerOpts = [AdvProtocols] ++ ServerOpts0, @@ -244,63 +243,24 @@ npn_not_supported_server(Config) when is_list(Config)-> %-------------------------------------------------------------------------------- npn_handshake_session_reused(Config) when is_list(Config)-> - ClientOpts0 = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = [{client_preferred_next_protocols, {client, [<<"http/1.0">>], <<"http/1.1">>}}] ++ ClientOpts0, - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), ServerOpts =[{next_protocols_advertised, [<<"spdy/2">>, <<"http/1.1">>, <<"http/1.0">>]}] ++ 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, session_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, {ssl_test_lib, no_result_msg, []}}, - {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} -> - ok; - {Client1, Other} -> - ct:fail(Other) - end, + ssl_test_lib:reuse_session(ClientOpts, ServerOpts, Config). - ssl_test_lib:close(Server), - ssl_test_lib:close(Client), - ssl_test_lib:close(Client1). - %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- run_npn_handshake(Config, ClientExtraOpts, ServerExtraOpts, ExpectedProtocol) -> Data = "hello world", - ClientOpts0 = ssl_test_lib:ssl_options(client_opts, Config), + ClientOpts0 = ssl_test_lib:ssl_options(client_rsa_verify_opts, Config), ClientOpts = ClientExtraOpts ++ ClientOpts0, - ServerOpts0 = ssl_test_lib:ssl_options(server_opts, Config), + ServerOpts0 = ssl_test_lib:ssl_options(server_rsa_opts, Config), ServerOpts = ServerExtraOpts ++ ServerOpts0, {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), diff --git a/lib/ssl/test/ssl_payload_SUITE.erl b/lib/ssl/test/ssl_payload_SUITE.erl index 1f9b6a5772..27b9c258a0 100644 --- a/lib/ssl/test/ssl_payload_SUITE.erl +++ b/lib/ssl/test/ssl_payload_SUITE.erl @@ -64,7 +64,8 @@ payload_tests() -> server_echos_active_huge, client_echos_passive_huge, client_echos_active_once_huge, - client_echos_active_huge]. + client_echos_active_huge, + client_active_once_server_close]. init_per_suite(Config) -> catch crypto:stop(), @@ -397,6 +398,23 @@ client_echos_active_huge(Config) when is_list(Config) -> client_echos_active( Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). + +%%-------------------------------------------------------------------- +client_active_once_server_close() -> + [{doc, "Server sends 500000 bytes and immediately after closes the connection" + "Make sure client recives all data if possible"}]. + +client_active_once_server_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), + %% + Data = binary:copy(<<"1234567890">>, 50000), + client_active_once_server_close( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname). + + + %%-------------------------------------------------------------------- %% Internal functions ------------------------------------------------ %%-------------------------------------------------------------------- @@ -541,42 +559,57 @@ client_echos_active( ssl_test_lib:close(Server), ssl_test_lib:close(Client). +client_active_once_server_close( + Data, ClientOpts, ServerOpts, ClientNode, ServerNode, Hostname) -> + Length = byte_size(Data), + Server = + ssl_test_lib:start_server( + [{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {?MODULE, send_close, [Data]}}, + {options, [{active, once}, {mode, binary} | 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, active_once_recv, [Length]}}, + {options,[{active, once}, {mode, binary} | ClientOpts]}]), + %% + ssl_test_lib:check_result(Server, ok, Client, ok). + +send(_Socket, _Data, 0, _) -> + ok; +send(Socket, Data, Count, RecvEcho) -> + ok = ssl:send(Socket, Data), + RecvEcho(), + send(Socket, Data, Count - 1, RecvEcho). -send(Socket, Data, Count, Verify) -> - send(Socket, Data, Count, <<>>, Verify). -%% -send(_Socket, _Data, 0, Acc, _Verify) -> - Acc; -send(Socket, Data, Count, Acc, Verify) -> +send_close(Socket, Data) -> ok = ssl:send(Socket, Data), - NewAcc = Verify(Acc), - send(Socket, Data, Count - 1, NewAcc, Verify). + ssl:close(Socket). - sender(Socket, Data) -> ct:log("Sender recv: ~p~n", [ssl:getopts(Socket, [active])]), - <<>> = - send( - Socket, Data, 100, - fun(Acc) -> verify_recv(Socket, Data, Acc) end), - ok. + send(Socket, Data, 100, + fun() -> + ssl_test_lib:recv_disregard(Socket, byte_size(Data)) + end). sender_active_once(Socket, Data) -> ct:log("Sender active once: ~p~n", [ssl:getopts(Socket, [active])]), - <<>> = - send( - Socket, Data, 100, - fun(Acc) -> verify_active_once(Socket, Data, Acc) end), - ok. + send(Socket, Data, 100, + fun() -> + ssl_test_lib:active_once_disregard(Socket, byte_size(Data)) + end). sender_active(Socket, Data) -> ct:log("Sender active: ~p~n", [ssl:getopts(Socket, [active])]), - <<>> = - send( - Socket, Data, 100, - fun(Acc) -> verify_active(Socket, Data, Acc) end), - ok. - + send(Socket, Data, 100, + fun() -> + ssl_test_lib:active_disregard(Socket, byte_size(Data)) + end). echoer(Socket, Size) -> ct:log("Echoer recv: ~p~n", [ssl:getopts(Socket, [active])]), @@ -592,99 +625,32 @@ echoer_active(Socket, Size) -> %% Receive Size bytes +echo_recv(_Socket, 0) -> + ok; echo_recv(Socket, Size) -> {ok, Data} = ssl:recv(Socket, 0), ok = ssl:send(Socket, Data), - NewSize = Size - byte_size(Data), - if - 0 < NewSize -> - echo_recv(Socket, NewSize); - 0 == NewSize -> - ok - end. - -%% Verify that received data is SentData, return any superflous data -verify_recv(Socket, SentData, Acc) -> - {ok, NewData} = ssl:recv(Socket, 0), - SentSize = byte_size(SentData), - NewAcc = <<Acc/binary, NewData/binary>>, - NewSize = byte_size(NewAcc), - if - SentSize < NewSize -> - {SentData,Rest} = split_binary(NewAcc, SentSize), - Rest; - NewSize < SentSize -> - verify_recv(Socket, SentData, NewAcc); - true -> - SentData = NewAcc, - <<>> - end. + echo_recv(Socket, Size - byte_size(Data)). %% Receive Size bytes +echo_active_once(_Socket, 0) -> + ok; echo_active_once(Socket, Size) -> receive {ssl, Socket, Data} -> ok = ssl:send(Socket, Data), NewSize = Size - byte_size(Data), ssl:setopts(Socket, [{active, once}]), - if - 0 < NewSize -> - echo_active_once(Socket, NewSize); - 0 == NewSize -> - ok - end + echo_active_once(Socket, NewSize) end. -%% Verify that received data is SentData, return any superflous data -verify_active_once(Socket, SentData, Acc) -> - receive - {ssl, Socket, Data} -> - SentSize = byte_size(SentData), - NewAcc = <<Acc/binary, Data/binary>>, - NewSize = byte_size(NewAcc), - ssl:setopts(Socket, [{active, once}]), - if - SentSize < NewSize -> - {SentData,Rest} = split_binary(NewAcc, SentSize), - Rest; - NewSize < SentSize -> - verify_active_once(Socket, SentData, NewAcc); - true -> - SentData = NewAcc, - <<>> - end - end. - - %% Receive Size bytes +echo_active(_Socket, 0) -> + ok; echo_active(Socket, Size) -> receive {ssl, Socket, Data} -> ok = ssl:send(Socket, Data), - NewSize = Size - byte_size(Data), - if - 0 < NewSize -> - echo_active(Socket, NewSize); - 0 == NewSize -> - ok - end - end. - -%% Verify that received data is SentData, return any superflous data -verify_active(Socket, SentData, Acc) -> - receive - {ssl, Socket, Data} -> - SentSize = byte_size(SentData), - NewAcc = <<Acc/binary, Data/binary>>, - NewSize = byte_size(NewAcc), - if - SentSize < NewSize -> - {SentData,Rest} = split_binary(NewAcc, SentSize), - Rest; - NewSize < SentSize -> - verify_active(Socket, SentData, NewAcc); - true -> - SentData = NewAcc, - <<>> - end - end. + echo_active(Socket, Size - byte_size(Data)) + end. + diff --git a/lib/ssl/test/ssl_pem_cache_SUITE.erl b/lib/ssl/test/ssl_pem_cache_SUITE.erl index 25d2cb300d..6f11e2bbe8 100644 --- a/lib/ssl/test/ssl_pem_cache_SUITE.erl +++ b/lib/ssl/test/ssl_pem_cache_SUITE.erl @@ -44,11 +44,8 @@ init_per_suite(Config0) -> try crypto:start() of ok -> ssl_test_lib:clean_start(), - %% make rsa certs using oppenssl - {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), - proplists:get_value(priv_dir, Config0)), - Config1 = ssl_test_lib:make_dsa_cert(Config0), - ssl_test_lib:cert_options(Config1) + %% make rsa certs + ssl_test_lib:make_rsa_cert(Config0) catch _:_ -> {skip, "Crypto did not start"} end. @@ -86,8 +83,8 @@ pem_cleanup() -> [{doc, "Test pem cache invalidate mechanism"}]. pem_cleanup(Config)when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = proplists:get_value(client_verification_opts, Config), - ServerOpts = proplists:get_value(server_verification_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = @@ -118,8 +115,8 @@ invalid_insert() -> invalid_insert(Config)when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = proplists:get_value(client_verification_opts, Config), - ServerOpts = proplists:get_value(server_verification_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), BadClientOpts = [{cacertfile, "tmp/does_not_exist.pem"} | proplists:delete(cacertfile, ClientOpts)], Server = diff --git a/lib/ssl/test/ssl_session_cache_SUITE.erl b/lib/ssl/test/ssl_session_cache_SUITE.erl index a0fab58b9d..7f33fe3204 100644 --- a/lib/ssl/test/ssl_session_cache_SUITE.erl +++ b/lib/ssl/test/ssl_session_cache_SUITE.erl @@ -48,7 +48,8 @@ all() -> session_cache_process_list, session_cache_process_mnesia, client_unique_session, - max_table_size + max_table_size, + save_specific_session ]. groups() -> @@ -60,10 +61,7 @@ init_per_suite(Config0) -> ok -> ssl_test_lib:clean_start(), %% make rsa certs using - {ok, _} = make_certs:all(proplists:get_value(data_dir, Config0), - proplists:get_value(priv_dir, Config0)), - Config = ssl_test_lib:make_dsa_cert(Config0), - ssl_test_lib:cert_options(Config) + ssl_test_lib:make_rsa_cert(Config0) catch _:_ -> {skip, "Crypto did not start"} end. @@ -97,7 +95,10 @@ init_per_testcase(session_cleanup, Config) -> init_per_testcase(client_unique_session, Config) -> ct:timetrap({seconds, 40}), Config; - +init_per_testcase(save_specific_session, Config) -> + ssl_test_lib:clean_start(), + ct:timetrap({seconds, 5}), + Config; init_per_testcase(max_table_size, Config) -> ssl:stop(), application:load(ssl), @@ -141,7 +142,7 @@ end_per_testcase(max_table_size, Config) -> end_per_testcase(default_action, Config); end_per_testcase(Case, Config) when Case == session_cache_process_list; Case == session_cache_process_mnesia -> - ets:delete(ssl_test), + catch ets:delete(ssl_test), Config; end_per_testcase(_, Config) -> Config. @@ -154,8 +155,8 @@ client_unique_session() -> "sets up many connections"}]. client_unique_session(Config) when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = proplists:get_value(client_opts, Config), - ServerOpts = proplists:get_value(server_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -164,8 +165,7 @@ client_unique_session(Config) when is_list(Config) -> {tcp_options, [{active, false}]}, {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), - LastClient = clients_start(Server, - ClientNode, Hostname, Port, ClientOpts, client_unique_session, 20), + LastClient = clients_start(Server, ClientNode, Hostname, Port, ClientOpts, 20), receive {LastClient, {ok, _}} -> ok @@ -185,8 +185,8 @@ session_cleanup() -> "does not grow and grow ..."}]. session_cleanup(Config) when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = ssl_test_lib:ssl_options(client_opts, Config), - ServerOpts = ssl_test_lib:ssl_options(server_opts, 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 = @@ -254,13 +254,75 @@ session_cache_process_mnesia(Config) when is_list(Config) -> session_cache_process(mnesia,Config). %%-------------------------------------------------------------------- +save_specific_session() -> + [{doc, "Test that we can save a specific client session" + }]. +save_specific_session(Config) when is_list(Config) -> + process_flag(trap_exit, true), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(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, []}}, + {tcp_options, [{active, false}]}, + {options, ServerOpts}]), + Port = ssl_test_lib:inet_port(Server), + + Client1 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, ClientOpts}]), + Server ! listen, + + Client2 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), + SessionID1 = + receive + {Client1, S1} -> + S1 + end, + + SessionID2 = + receive + {Client2, S2} -> + S2 + end, + + true = SessionID1 =/= SessionID2, + + {status, _, _, StatusInfo} = sys:get_status(whereis(ssl_manager)), + [_, _,_, _, Prop] = StatusInfo, + State = ssl_test_lib:state(Prop), + ClientCache = element(2, State), + 2 = ssl_session_cache:size(ClientCache), + + Server ! listen, + + Client3 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_session, SessionID2} | ClientOpts]}]), + receive + {Client3, SessionID2} -> + ok; + {Client3, SessionID3}-> + ct:fail({got, SessionID3, expected, SessionID2}); + Other -> + ct:fail({got,Other}) + end. + +%%-------------------------------------------------------------------- max_table_size() -> [{doc,"Test max limit on session table"}]. max_table_size(Config) when is_list(Config) -> process_flag(trap_exit, true), - ClientOpts = proplists:get_value(client_verification_opts, Config), - ServerOpts = proplists:get_value(server_verification_opts, Config), + ClientOpts = proplists:get_value(client_rsa_verify_opts, Config), + ServerOpts = proplists:get_value(server_rsa_verify_opts, Config), {ClientNode, ServerNode, Hostname} = ssl_test_lib:run_where(Config), Server = ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, @@ -270,7 +332,7 @@ max_table_size(Config) when is_list(Config) -> {options, ServerOpts}]), Port = ssl_test_lib:inet_port(Server), LastClient = clients_start(Server, - ClientNode, Hostname, Port, ClientOpts, max_table_size, 20), + ClientNode, Hostname, Port, ClientOpts, 20), receive {LastClient, {ok, _}} -> ok @@ -426,25 +488,27 @@ session_loop(Sess) -> %%-------------------------------------------------------------------- session_cache_process(_Type,Config) when is_list(Config) -> - ssl_basic_SUITE:reuse_session(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). -clients_start(_Server, ClientNode, Hostname, Port, ClientOpts, Test, 0) -> +clients_start(_Server, ClientNode, Hostname, Port, ClientOpts, 0) -> %% Make sure session is registered ct:sleep(?SLEEP * 2), ssl_test_lib:start_client([{node, ClientNode}, {port, Port}, {host, Hostname}, {mfa, {?MODULE, connection_info_result, []}}, - {from, self()}, {options, test_copts(Test, 0, ClientOpts)}]); -clients_start(Server, ClientNode, Hostname, Port, ClientOpts, Test, N) -> + {from, self()}, {options, ClientOpts}]); +clients_start(Server, ClientNode, Hostname, Port, ClientOpts, N) -> spawn_link(ssl_test_lib, start_client, [[{node, ClientNode}, {port, Port}, {host, Hostname}, {mfa, {ssl_test_lib, no_result, []}}, - {from, self()}, {options, test_copts(Test, N, ClientOpts)}]]), + {from, self()}, {options, ClientOpts}]]), Server ! listen, wait_for_server(), - clients_start(Server, ClientNode, Hostname, Port, ClientOpts, Test, N-1). + clients_start(Server, ClientNode, Hostname, Port, ClientOpts, N-1). connection_info_result(Socket) -> ssl:connection_information(Socket, [protocol, cipher_suite]). @@ -481,21 +545,3 @@ get_delay_timers() -> wait_for_server() -> ct:sleep(100). - - -test_copts(_, 0, ClientOpts) -> - ClientOpts; -test_copts(max_table_size, N, ClientOpts) -> - Version = tls_record:highest_protocol_version([]), - CipherSuites = %%lists:map(fun(X) -> ssl_cipher_format:suite_definition(X) end, ssl_cipher:filter_suites(ssl_cipher:suites(Version))), -[ Y|| Y = {Alg,_, _, _} <- lists:map(fun(X) -> ssl_cipher_format:suite_definition(X) end, ssl_cipher:filter_suites(ssl_cipher:suites(Version))), Alg =/= ecdhe_ecdsa, Alg =/= ecdh_ecdsa, Alg =/= ecdh_rsa, Alg =/= ecdhe_rsa, Alg =/= dhe_dss, Alg =/= dss], - case length(CipherSuites) of - M when M >= N -> - Cipher = lists:nth(N, CipherSuites), - ct:pal("~p",[Cipher]), - [{ciphers, [Cipher]} | ClientOpts]; - _ -> - ClientOpts - end; -test_copts(_, _, ClientOpts) -> - ClientOpts. diff --git a/lib/ssl/test/ssl_test_lib.erl b/lib/ssl/test/ssl_test_lib.erl index 7767d76a0d..681bc14f10 100644 --- a/lib/ssl/test/ssl_test_lib.erl +++ b/lib/ssl/test/ssl_test_lib.erl @@ -523,7 +523,7 @@ cert_options(Config) -> {client_verification_opts, [{cacertfile, ServerCaCertFile}, {certfile, ClientCertFile}, {keyfile, ClientKeyFile}, - {ssl_imp, new}]}, + {verify, verify_peer}]}, {client_verification_opts_digital_signature_only, [{cacertfile, ServerCaCertFile}, {certfile, ClientCertFileDigitalSignatureOnly}, {keyfile, ClientKeyFile}, @@ -1583,35 +1583,22 @@ v_1_2_check(ecdh_rsa, ecdh_ecdsa) -> v_1_2_check(_, _) -> false. -send_recv_result_active(Socket) -> - ssl:send(Socket, "Hello world"), - receive - {ssl, Socket, "H"} -> - receive - {ssl, Socket, "ello world"} -> - ok - end; - {ssl, Socket, "Hello world"} -> - ok - end. - send_recv_result(Socket) -> - ssl:send(Socket, "Hello world"), - {ok,"Hello world"} = ssl:recv(Socket, 11), + Data = "Hello world", + ssl:send(Socket, Data), + {ok, Data} = ssl:recv(Socket, length(Data)), + ok. + +send_recv_result_active(Socket) -> + Data = "Hello world", + ssl:send(Socket, Data), + Data = active_recv(Socket, length(Data)), ok. send_recv_result_active_once(Socket) -> - ssl:send(Socket, "Hello world"), - receive - {ssl, Socket, "H"} -> - ssl:setopts(Socket, [{active, once}]), - receive - {ssl, Socket, "ello world"} -> - ok - end; - {ssl, Socket, "Hello world"} -> - ok - end. + Data = "Hello world", + ssl:send(Socket, Data), + active_once_recv_list(Socket, length(Data)). active_recv(Socket, N) -> active_recv(Socket, N, []). @@ -1624,6 +1611,44 @@ active_recv(Socket, N, Acc) -> active_recv(Socket, N-length(Bytes), Acc ++ Bytes) end. +active_once_recv(_Socket, 0) -> + ok; +active_once_recv(Socket, N) -> + receive + {ssl, Socket, Bytes} -> + ssl:setopts(Socket, [{active, once}]), + active_once_recv(Socket, N-byte_size(Bytes)) + end. + +active_once_recv_list(_Socket, 0) -> + ok; +active_once_recv_list(Socket, N) -> + receive + {ssl, Socket, Bytes} -> + ssl:setopts(Socket, [{active, once}]), + active_once_recv_list(Socket, N-length(Bytes)) + end. +recv_disregard(_Socket, 0) -> + ok; +recv_disregard(Socket, N) -> + {ok, Bytes} = ssl:recv(Socket, 0), + recv_disregard(Socket, N-byte_size(Bytes)). + +active_disregard(_Socket, 0) -> + ok; +active_disregard(Socket, N) -> + receive + {ssl, Socket, Bytes} -> + active_disregard(Socket, N-byte_size(Bytes)) + end. +active_once_disregard(_Socket, 0) -> + ok; +active_once_disregard(Socket, N) -> + receive + {ssl, Socket, Bytes} -> + ssl:setopts(Socket, [{active, once}]), + active_once_disregard(Socket, N-byte_size(Bytes)) + end. is_sane_ecc(openssl) -> case os:cmd("openssl version") of "OpenSSL 1.0.0a" ++ _ -> % Known bug in openssl @@ -2161,3 +2186,98 @@ server_msg(Server, ServerMsg) -> Unexpected -> ct:fail(Unexpected) end. + +session_id(Socket) -> + {ok, [{session_id, ID}]} = ssl:connection_information(Socket, [session_id]), + ID. + +reuse_session(ClientOpts, ServerOpts, 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, + + Client2 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port0}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, false} + | ClientOpts]}]), + receive + {Client2, SID} -> + ct:fail(session_reused_when_session_reuse_disabled_by_client); + {Client2, _} -> + ok + end, + + ssl_test_lib:close(Server0), + ssl_test_lib:close(Client0), + ssl_test_lib:close(Client1), + ssl_test_lib:close(Client2), + + Server1 = + ssl_test_lib:start_server([{node, ServerNode}, {port, 0}, + {from, self()}, + {mfa, {ssl_test_lib, no_result, []}}, + {tcp_options, [{active, false}]}, + {options, [{reuse_sessions, false} |ServerOpts]}]), + Port1 = ssl_test_lib:inet_port(Server1), + + Client3 = ssl_test_lib:start_client([{node, ClientNode}, + {port, Port1}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, [{reuse_sessions, save} | ClientOpts]}]), + SID1 = receive + {Client3, Id3} -> + Id3 + end, + + Server1 ! listen, + + Client4 = + ssl_test_lib:start_client([{node, ClientNode}, + {port, Port1}, {host, Hostname}, + {mfa, {ssl_test_lib, session_id, []}}, + {from, self()}, {options, ClientOpts}]), + + receive + {Client4, SID1} -> + ct:fail(session_reused_when_session_reuse_disabled_by_server); + {Client4, _} -> + ok + end, + + ssl_test_lib:close(Server1), + ssl_test_lib:close(Client3), + ssl_test_lib:close(Client4). + diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index 3c8b25b912..9a18ea4d81 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -260,8 +260,9 @@ special_init(TestCase, Config) when 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_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); @@ -761,8 +762,8 @@ 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_opts, Config), - ClientOpts = ssl_test_lib:ssl_options(client_rsa_opts, Config), + 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), @@ -771,12 +772,14 @@ erlang_client_openssl_server_renegotiate(Config) when is_list(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), 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), @@ -806,7 +809,7 @@ 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_opts, Config), + 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), @@ -815,6 +818,7 @@ erlang_client_openssl_server_renegotiate_after_client_data(Config) when is_list( 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), @@ -822,6 +826,7 @@ erlang_client_openssl_server_renegotiate_after_client_data(Config) when is_list( 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), @@ -856,7 +861,7 @@ erlang_client_openssl_server_nowrap_seqnum() -> " 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_opts, Config), + 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), @@ -865,12 +870,14 @@ erlang_client_openssl_server_nowrap_seqnum(Config) when is_list(Config) -> 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), @@ -899,7 +906,7 @@ erlang_server_openssl_client_nowrap_seqnum() -> " 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_opts, Config), + ServerOpts = ssl_test_lib:ssl_options(server_rsa_verify_opts, Config), {_, ServerNode, Hostname} = ssl_test_lib:run_where(Config), @@ -1835,6 +1842,7 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac 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), @@ -1842,6 +1850,7 @@ start_erlang_client_and_openssl_server_for_npn_negotiation(Config, Data, Callbac 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), diff --git a/lib/stdlib/doc/src/lists.xml b/lib/stdlib/doc/src/lists.xml index 66146e9258..2755fb3dce 100644 --- a/lib/stdlib/doc/src/lists.xml +++ b/lib/stdlib/doc/src/lists.xml @@ -4,7 +4,7 @@ <erlref> <header> <copyright> - <year>1996</year><year>2018</year> + <year>1996</year><year>2019</year> <holder>Ericsson AB. All Rights Reserved.</holder> </copyright> <legalnotice> @@ -689,6 +689,18 @@ c</pre> </func> <func> + <name name="search" arity="2" since="OTP 21.0"/> + <fsummary>Find the first element that satisfies a predicate.</fsummary> + <desc> + <p>If there is a <c><anno>Value</anno></c> in <c><anno>List</anno></c> + such that <c><anno>Pred</anno>(<anno>Value</anno>)</c> returns + <c>true</c>, returns <c>{value, <anno>Value</anno>}</c> + for the first such <c><anno>Value</anno></c>, + otherwise returns <c>false</c>.</p> + </desc> + </func> + + <func> <name name="seq" arity="2" since=""/> <name name="seq" arity="3" since=""/> <fsummary>Generate a sequence of integers.</fsummary> @@ -771,18 +783,6 @@ length(lists:seq(From, To, Incr)) =:= (To - From + Incr) div Incr</code> </func> <func> - <name name="search" arity="2" since="OTP 21.0"/> - <fsummary>Find the first element that satisfies a predicate.</fsummary> - <desc> - <p>If there is a <c><anno>Value</anno></c> in <c><anno>List</anno></c> - such that <c><anno>Pred</anno>(<anno>Value</anno>)</c> returns - <c>true</c>, returns <c>{value, <anno>Value</anno>}</c> - for the first such <c><anno>Value</anno></c>, - otherwise returns <c>false</c>.</p> - </desc> - </func> - - <func> <name name="splitwith" arity="2" since=""/> <fsummary>Split a list into two lists based on a predicate.</fsummary> <desc> diff --git a/lib/stdlib/src/ms_transform.erl b/lib/stdlib/src/ms_transform.erl index 6d243e1bec..97ec785c62 100644 --- a/lib/stdlib/src/ms_transform.erl +++ b/lib/stdlib/src/ms_transform.erl @@ -556,8 +556,8 @@ tg({call, Line, {remote,_,{atom,_,erlang},{atom, Line2, FunName}},ParaList}, FunName,length(ParaList)}}) end; tg({call, Line, {remote,_,{atom,_,ModuleName}, - {atom, _, FunName}},_ParaList},B) -> - throw({error,Line,{?ERR_GENREMOTECALL+B#tgd.eb,ModuleName,FunName}}); + {atom, _, FunName}},ParaList},B) -> + throw({error,Line,{?ERR_GENREMOTECALL+B#tgd.eb,ModuleName,FunName,length(ParaList)}}); tg({cons,Line, H, T},B) -> {cons, Line, tg(H,B), tg(T,B)}; tg({nil, Line},_B) -> diff --git a/lib/xmerl/doc/src/notes.xml b/lib/xmerl/doc/src/notes.xml index a97036127e..a05eede523 100644 --- a/lib/xmerl/doc/src/notes.xml +++ b/lib/xmerl/doc/src/notes.xml @@ -62,6 +62,21 @@ </section> +<section><title>Xmerl 1.3.16.1</title> + + <section><title>Fixed Bugs and Malfunctions</title> + <list> + <item> + <p>The charset detection parsing crash in some cases when + the XML directive is not syntactic correct.</p> + <p> + Own Id: OTP-15492 Aux Id: ERIERL-283 </p> + </item> + </list> + </section> + +</section> + <section><title>Xmerl 1.3.16</title> <section><title>Fixed Bugs and Malfunctions</title> @@ -1412,4 +1427,3 @@ </section> </section> </chapter> - diff --git a/lib/xmerl/src/xmerl_sax_parser.erl b/lib/xmerl/src/xmerl_sax_parser.erl index e383c4c349..fe836fd8cd 100644 --- a/lib/xmerl/src/xmerl_sax_parser.erl +++ b/lib/xmerl/src/xmerl_sax_parser.erl @@ -1,8 +1,8 @@ %%-------------------------------------------------------------------- %% %CopyrightBegin% -%% -%% Copyright Ericsson AB 2008-2017. All Rights Reserved. -%% +%% +%% 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 @@ -14,13 +14,13 @@ %% 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% %%---------------------------------------------------------------------- %% File : xmerl_sax_parser.erl %% Description : XML SAX parse API module. %% -%% Created : 4 Jun 2008 +%% Created : 4 Jun 2008 %%---------------------------------------------------------------------- -module(xmerl_sax_parser). @@ -72,9 +72,9 @@ file(Name,Options) -> CL = filename:absname(Dir), File = filename:basename(Name), ContinuationFun = fun default_continuation_cb/1, - Res = stream(<<>>, + Res = stream(<<>>, [{continuation_fun, ContinuationFun}, - {continuation_state, FD}, + {continuation_state, FD}, {current_location, CL}, {entity, File} |Options], @@ -101,39 +101,39 @@ stream(Xml, Options, InputType) when is_list(Xml), is_list(Options) -> State = parse_options(Options, initial_state()), case State#xmerl_sax_parser_state.file_type of dtd -> - xmerl_sax_parser_list:parse_dtd(Xml, + xmerl_sax_parser_list:parse_dtd(Xml, State#xmerl_sax_parser_state{encoding = list, input_type = InputType}); normal -> - xmerl_sax_parser_list:parse(Xml, + xmerl_sax_parser_list:parse(Xml, State#xmerl_sax_parser_state{encoding = list, input_type = InputType}) end; stream(Xml, Options, InputType) when is_binary(Xml), is_list(Options) -> - case parse_options(Options, initial_state()) of + case parse_options(Options, initial_state()) of {error, Reason} -> {error, Reason}; State -> - ParseFunction = + ParseFunction = case State#xmerl_sax_parser_state.file_type of dtd -> parse_dtd; normal -> parse end, - try + try {Xml1, State1} = detect_charset(Xml, State), parse_binary(Xml1, State1#xmerl_sax_parser_state{input_type = InputType}, ParseFunction) catch throw:{fatal_error, {State2, Reason}} -> - {fatal_error, + {fatal_error, { State2#xmerl_sax_parser_state.current_location, - State2#xmerl_sax_parser_state.entity, + State2#xmerl_sax_parser_state.entity, 1 }, - Reason, [], + Reason, [], State2#xmerl_sax_parser_state.event_state} end end. @@ -157,7 +157,7 @@ parse_binary(Xml, #xmerl_sax_parser_state{encoding={utf16,big}}=State, F) -> xmerl_sax_parser_utf16be:F(Xml, State); parse_binary(Xml, #xmerl_sax_parser_state{encoding=latin1}=State, F) -> xmerl_sax_parser_latin1:F(Xml, State); -parse_binary(_, #xmerl_sax_parser_state{encoding=Enc}, State) -> +parse_binary(_, #xmerl_sax_parser_state{encoding=Enc}, State) -> ?fatal_error(State, lists:flatten(io_lib:format("Charcter set ~p not supported", [Enc]))). %%---------------------------------------------------------------------- @@ -177,9 +177,9 @@ initial_state() -> %%---------------------------------------------------------------------- %% Function: parse_options(Options, State) %% Input: Options = [Option] -%% Option = {event_state, term()} | {event_fun, fun()} | +%% Option = {event_state, term()} | {event_fun, fun()} | %% {continuation_state, term()} | {continuation_fun, fun()} | -%% {encoding, Encoding} | {file_type, FT} +%% {encoding, Encoding} | {file_type, FT} %% FT = normal | dtd %% Encoding = utf8 | utf16le | utf16be | list | iso8859 %% State = #xmerl_sax_parser_state{} @@ -200,7 +200,7 @@ parse_options([{file_type, FT} |Options], State) when FT==normal; FT==dtd -> parse_options(Options, State#xmerl_sax_parser_state{file_type = FT}); parse_options([{encoding, E} |Options], State) -> case check_encoding_option(E) of - {error, Reason} -> + {error, Reason} -> {error, Reason}; Enc -> parse_options(Options, State#xmerl_sax_parser_state{encoding = Enc}) @@ -231,7 +231,7 @@ check_encoding_option(E) -> %% Description: Detects which character set is used in a binary stream. %%---------------------------------------------------------------------- detect_charset(<<>>, #xmerl_sax_parser_state{continuation_fun = undefined} = State) -> - ?fatal_error(State, "Can't detect character encoding due to lack of indata"); + ?fatal_error(State, "Can't detect character encoding due to lack of indata"); detect_charset(<<>>, State) -> cf(<<>>, State, fun detect_charset/2); detect_charset(Bytes, State) -> @@ -269,22 +269,14 @@ detect_charset_1(<<16#3C, 16#3F, 16#78, 16#6D>> = Xml, State) -> cf(Xml, State, fun detect_charset_1/2); detect_charset_1(<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml2/binary>>, State) -> {Xml3, State1} = read_until_end_of_xml_directive(Xml2, State), - case parse_xml_directive(Xml3) of - {error, Reason} -> - ?fatal_error(State, Reason); - AttrList -> - case lists:keysearch("encoding", 1, AttrList) of - {value, {_, E}} -> - case convert_encoding(E) of - {error, Reason} -> - ?fatal_error(State, Reason); - Enc -> - {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>, - State1#xmerl_sax_parser_state{encoding=Enc}} - end; - _ -> - {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>, State1} - end + AttrList = parse_xml_directive(Xml3, State), + case lists:keysearch("encoding", 1, AttrList) of + {value, {_, E}} -> + Enc = convert_encoding(E, State), + {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>, + State1#xmerl_sax_parser_state{encoding=Enc}}; + _ -> + {<<16#3C, 16#3F, 16#78, 16#6D, 16#6C, Xml3/binary>>, State1} end; detect_charset_1(Xml, State) -> {Xml, State}. @@ -295,7 +287,7 @@ detect_charset_1(Xml, State) -> %% Output: utf8 | iso8859 %% Description: Converting 7,8 bit and utf8 encoding strings to internal format. %%---------------------------------------------------------------------- -convert_encoding(Enc) -> %% Just for 7,8 bit + utf8 +convert_encoding(Enc, State) -> %% Just for 7,8 bit + utf8 case string:to_lower(Enc) of "utf-8" -> utf8; "us-ascii" -> utf8; @@ -309,19 +301,19 @@ convert_encoding(Enc) -> %% Just for 7,8 bit + utf8 "iso-8859-7" -> latin1; "iso-8859-8" -> latin1; "iso-8859-9" -> latin1; - _ -> {error, "Unknown encoding: " ++ Enc} + _ -> ?fatal_error(State, "Unknown encoding: " ++ Enc) end. %%---------------------------------------------------------------------- %% Function: parse_xml_directive(Xml) %% Input: Xml = binary() %% Acc = list() -%% Output: +%% Output: %% Description: Parsing the xml declaration from the input stream. %%---------------------------------------------------------------------- -parse_xml_directive(<<C, Rest/binary>>) when ?is_whitespace(C) -> - parse_xml_directive_1(Rest, []). - +parse_xml_directive(<<C, Rest/binary>>, State) when ?is_whitespace(C) -> + parse_xml_directive_1(Rest, [], State). + %%---------------------------------------------------------------------- %% Function: parse_xml_directive_1(Xml, Acc) -> [{Name, Value}] %% Input: Xml = binary() @@ -331,20 +323,20 @@ parse_xml_directive(<<C, Rest/binary>>) when ?is_whitespace(C) -> %% Output: see above %% Description: Parsing the xml declaration from the input stream. %%---------------------------------------------------------------------- -parse_xml_directive_1(<<C, Rest/binary>>, Acc) when ?is_whitespace(C) -> - parse_xml_directive_1(Rest, Acc); -parse_xml_directive_1(<<"?>", _/binary>>, Acc) -> +parse_xml_directive_1(<<C, Rest/binary>>, Acc, State) when ?is_whitespace(C) -> + parse_xml_directive_1(Rest, Acc, State); +parse_xml_directive_1(<<"?>", _/binary>>, Acc, _State) -> Acc; -parse_xml_directive_1(<<C, Rest/binary>>, Acc) when 97 =< C, C =< 122 -> +parse_xml_directive_1(<<C, Rest/binary>>, Acc, State) when 97 =< C, C =< 122 -> {Name, Rest1} = parse_name(Rest, [C]), - Rest2 = parse_eq(Rest1), - {Value, Rest3} = parse_value(Rest2), - parse_xml_directive_1(Rest3, [{Name, Value} |Acc]); -parse_xml_directive_1(_, _) -> - {error, "Unknown attribute in xml directive"}. + Rest2 = parse_eq(Rest1, State), + {Value, Rest3} = parse_value(Rest2, State), + parse_xml_directive_1(Rest3, [{Name, Value} |Acc], State); +parse_xml_directive_1(_, _, State) -> + ?fatal_error(State, "Unknown attribute in xml directive"). %%---------------------------------------------------------------------- -%% Function: parse_xml_directive_1(Xml, Acc) -> Name +%% Function: parse_name(Xml, Acc) -> Name %% Input: Xml = binary() %% Acc = string() %% Output: Name = string() @@ -361,10 +353,12 @@ parse_name(Rest, Acc) -> %% Output: Rest = binary() %% Description: Reads an '=' from the stream. %%---------------------------------------------------------------------- -parse_eq(<<C, Rest/binary>>) when ?is_whitespace(C) -> - parse_eq(Rest); -parse_eq(<<"=", Rest/binary>>) -> - Rest. +parse_eq(<<C, Rest/binary>>, State) when ?is_whitespace(C) -> + parse_eq(Rest, State); +parse_eq(<<"=", Rest/binary>>, _State) -> + Rest; +parse_eq(_, State) -> + ?fatal_error(State, "expecting = or whitespace"). %%---------------------------------------------------------------------- %% Function: parse_value(Xml) -> {Value, Rest} @@ -373,10 +367,12 @@ parse_eq(<<"=", Rest/binary>>) -> %% Rest = binary() %% Description: Parsing an attribute value from the stream. %%---------------------------------------------------------------------- -parse_value(<<C, Rest/binary>>) when ?is_whitespace(C) -> - parse_value(Rest); -parse_value(<<C, Rest/binary>>) when C == $'; C == $" -> - parse_value_1(Rest, C, []). +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(_, State) -> + ?fatal_error(State, "\', \" or whitespace expected"). %%---------------------------------------------------------------------- %% Function: parse_value_1(Xml, Stop, Acc) -> {Value, Rest} @@ -431,7 +427,7 @@ read_until_end_of_xml_directive(Rest, State) -> nomatch -> case cf(Rest, State) of {<<>>, _} -> - ?fatal_error(State, "Can't detect character encoding due to lack of indata"); + ?fatal_error(State, "Can't detect character encoding due to lack of indata"); {NewBytes, NewState} -> read_until_end_of_xml_directive(NewBytes, NewState) end; @@ -450,9 +446,9 @@ read_until_end_of_xml_directive(Rest, State) -> %% input stream and calls the fun in NextCall. %%---------------------------------------------------------------------- cf(_Rest, #xmerl_sax_parser_state{continuation_fun = undefined} = State) -> - ?fatal_error(State, "Continuation function undefined"); + ?fatal_error(State, "Continuation function undefined"); cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = CState} = State) -> - Result = + Result = try CFun(CState) catch @@ -463,9 +459,9 @@ cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = C end, case Result of {<<>>, _} -> - ?fatal_error(State, "Can't detect character encoding due to lack of indata"); + ?fatal_error(State, "Can't detect character encoding due to lack of indata"); {NewBytes, NewContState} -> - {<<Rest/binary, NewBytes/binary>>, + {<<Rest/binary, NewBytes/binary>>, State#xmerl_sax_parser_state{continuation_state = NewContState}} end. @@ -479,10 +475,10 @@ cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = C %% input stream and calls the fun in NextCall. %%---------------------------------------------------------------------- cf(_Rest, #xmerl_sax_parser_state{continuation_fun = undefined} = State, _) -> - ?fatal_error(State, "Continuation function undefined"); -cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = CState} = State, + ?fatal_error(State, "Continuation function undefined"); +cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = CState} = State, NextCall) -> - Result = + Result = try CFun(CState) catch @@ -493,8 +489,8 @@ cf(Rest, #xmerl_sax_parser_state{continuation_fun = CFun, continuation_state = C end, case Result of {<<>>, _} -> - ?fatal_error(State, "Can't detect character encoding due to lack of indata"); + ?fatal_error(State, "Can't detect character encoding due to lack of indata"); {NewBytes, NewContState} -> - NextCall(<<Rest/binary, NewBytes/binary>>, + NextCall(<<Rest/binary, NewBytes/binary>>, State#xmerl_sax_parser_state{continuation_state = NewContState}) end. |