aboutsummaryrefslogtreecommitdiffstats
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/common_test/test/ct_test_support.erl4
-rw-r--r--lib/dialyzer/src/Makefile2
-rw-r--r--lib/dialyzer/src/dialyzer_analysis_callgraph.erl6
-rw-r--r--lib/dialyzer/src/dialyzer_dataflow.erl14
-rw-r--r--lib/dialyzer/src/dialyzer_gui_wx.erl57
-rw-r--r--lib/dialyzer/test/small_SUITE_data/src/maybe_servers.erl31
-rw-r--r--lib/dialyzer/vsn.mk2
-rw-r--r--lib/mnesia/src/mnesia_loader.erl2
-rw-r--r--lib/runtime_tools/test/Makefile1
-rw-r--r--lib/runtime_tools/test/dyntrace_SUITE.erl224
-rw-r--r--lib/ssl/doc/src/ssl.xml23
-rw-r--r--lib/ssl/src/ssl.erl15
-rw-r--r--lib/ssl/src/ssl_connection.erl39
-rw-r--r--lib/ssl/src/ssl_handshake.erl14
-rw-r--r--lib/ssl/src/ssl_tls1.erl2
15 files changed, 393 insertions, 43 deletions
diff --git a/lib/common_test/test/ct_test_support.erl b/lib/common_test/test/ct_test_support.erl
index 62c167d78b..a712ab6cb9 100644
--- a/lib/common_test/test/ct_test_support.erl
+++ b/lib/common_test/test/ct_test_support.erl
@@ -1013,8 +1013,8 @@ result_match({SkipOrFail,{ErrorInd,{EMod,EFunc,{Why,'_'}}}},
true;
result_match({failed,{timetrap_timeout,{'$approx',Num}}},
{failed,{timetrap_timeout,Value}}) ->
- if Value >= trunc(Num-0.01*Num),
- Value =< trunc(Num+0.01*Num) -> true;
+ if Value >= trunc(Num-0.02*Num),
+ Value =< trunc(Num+0.02*Num) -> true;
true -> false
end;
result_match(Result, Result) ->
diff --git a/lib/dialyzer/src/Makefile b/lib/dialyzer/src/Makefile
index 810f86dc21..97c5469d39 100644
--- a/lib/dialyzer/src/Makefile
+++ b/lib/dialyzer/src/Makefile
@@ -86,7 +86,7 @@ APPUP_TARGET= $(EBIN)/$(APPUP_FILE)
ifeq ($(NATIVE_LIBS_ENABLED),yes)
ERL_COMPILE_FLAGS += +native
endif
-ERL_COMPILE_FLAGS += +warn_exported_vars +warn_unused_import +warn_untyped_record +warn_missing_spec
+ERL_COMPILE_FLAGS += +warn_exported_vars +warn_unused_import +warn_untyped_record +warn_missing_spec +warnings_as_errors
# ----------------------------------------------------
# Targets
diff --git a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
index 5be870d78f..b6dbfdfacf 100644
--- a/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
+++ b/lib/dialyzer/src/dialyzer_analysis_callgraph.erl
@@ -463,8 +463,10 @@ default_includes(Dir) ->
rcv_and_send_ext_types(Parent) ->
Self = self(),
Self ! {Self, done},
- ExtTypes = rcv_ext_types(Self, []),
- Parent ! {Self, ext_types, ExtTypes},
+ case rcv_ext_types(Self, []) of
+ [] -> ok;
+ ExtTypes -> Parent ! {Self, ext_types, ExtTypes}
+ end,
ok.
rcv_ext_types(Self, ExtTypes) ->
diff --git a/lib/dialyzer/src/dialyzer_dataflow.erl b/lib/dialyzer/src/dialyzer_dataflow.erl
index aba13278ff..d1dd7e1c34 100644
--- a/lib/dialyzer/src/dialyzer_dataflow.erl
+++ b/lib/dialyzer/src/dialyzer_dataflow.erl
@@ -761,7 +761,13 @@ handle_apply_or_call([{TypeOfApply, {Fun, Sig, Contr, LocalRet}}|Left],
true -> AccArgTypes;
false -> [t_sup(X, Y) || {X, Y} <- lists:zip(NewArgTypes, AccArgTypes)]
end,
- NewAccRet = t_sup(AccRet, t_inf(RetWithoutLocal, LocalRet, opaque)),
+ TotalRet =
+ case t_is_none(LocalRet) andalso t_is_unit(RetWithoutLocal) of
+ true -> RetWithoutLocal;
+ false -> t_inf(RetWithoutLocal, LocalRet, opaque)
+ end,
+ NewAccRet = t_sup(AccRet, TotalRet),
+ ?debug("NewAccRet: ~s\n", [t_to_string(NewAccRet)]),
handle_apply_or_call(Left, Args, ArgTypes, Map, Tree,
State3, NewAccArgTypes, NewAccRet);
handle_apply_or_call([], Args, _ArgTypes, Map, _Tree, State,
@@ -3109,6 +3115,7 @@ init_fun_tab([Fun|Left], Dict, TreeMap, Callgraph, Plt, Opaques) ->
NewDict = dict:store(Fun, FunEntry, Dict),
init_fun_tab(Left, NewDict, TreeMap, Callgraph, Plt, Opaques);
init_fun_tab([], Dict, _TreeMap, _Callgraph, _Plt, _Opaques) ->
+ ?debug("DICT:~p\n",[dict:to_list(Dict)]),
Dict.
state__update_fun_env(Tree, Map, #state{envs = Envs} = State) ->
@@ -3140,7 +3147,9 @@ state__fun_type(Fun, #state{fun_tab = FunTab}) ->
if is_integer(Fun) -> Fun;
true -> get_label(Fun)
end,
- case dict:find(Label, FunTab) of
+ Entry = dict:find(Label, FunTab),
+ ?debug("FunType ~p:~p\n",[Label, Entry]),
+ case Entry of
{ok, {not_handled, {A, R}}} ->
t_fun(A, R);
{ok, {A, R}} ->
@@ -3248,6 +3257,7 @@ state__fun_info(Fun, #state{callgraph = CG, fun_tab = FunTab, plt = PLT}) ->
{not_handled, {_Args, Ret}} -> Ret;
{_Args, Ret} -> Ret
end,
+ ?debug("LocalRet: ~s\n", [t_to_string(LocalRet)]),
{Fun, Sig, Contract, LocalRet}.
state__find_apply_return(Tree, #state{callgraph = Callgraph} = State) ->
diff --git a/lib/dialyzer/src/dialyzer_gui_wx.erl b/lib/dialyzer/src/dialyzer_gui_wx.erl
index 9ff32bd8b1..8f470dd650 100644
--- a/lib/dialyzer/src/dialyzer_gui_wx.erl
+++ b/lib/dialyzer/src/dialyzer_gui_wx.erl
@@ -40,39 +40,38 @@
%%------------------------------------------------------------------------
--type wx_object() :: any(). %% XXX: should be imported from wx
-
--record(menu, {file :: wx_object(),
- warnings :: wx_object(),
- plt :: wx_object(),
- options :: wx_object(),
- help :: wx_object()}).
-
--record(gui_state, {add :: wx_object(),
- add_dir :: wx_object(),
- add_rec :: wx_object(),
- chosen_box :: wx_object(),
+-record(menu, {file :: wx:wx_object(),
+ warnings :: wx:wx_object(),
+ plt :: wx:wx_object(),
+ options :: wx:wx_object(),
+ help :: wx:wx_object()}).
+-type menu() :: #menu{}.
+
+-record(gui_state, {add :: wx:wx_object(),
+ add_dir :: wx:wx_object(),
+ add_rec :: wx:wx_object(),
+ chosen_box :: wx:wx_object(),
analysis_pid :: pid(),
- del_file :: wx_object(),
+ del_file :: wx:wx_object(),
doc_plt :: dialyzer_plt:plt(),
- clear_chosen :: wx_object(),
- clear_log :: wx_object(),
- explain_warn :: wx_object(),
- clear_warn :: wx_object(),
+ clear_chosen :: wx:wx_object(),
+ clear_log :: wx:wx_object(),
+ explain_warn :: wx:wx_object(),
+ clear_warn :: wx:wx_object(),
init_plt :: dialyzer_plt:plt(),
- dir_entry :: wx_object(),
- file_box :: wx_object(),
+ dir_entry :: wx:wx_object(),
+ file_box :: wx:wx_object(),
files_to_analyze :: ordset(string()),
- gui :: wx_object(),
- log :: wx_object(),
- menu :: #menu{},
- mode :: wx_object(),
+ gui :: wx:wx_object(),
+ log :: wx:wx_object(),
+ menu :: menu(),
+ mode :: wx:wx_object(),
options :: #options{},
- run :: wx_object(),
- stop :: wx_object(),
- frame :: wx_object(),
- warnings_box :: wx_object(),
- explanation_box :: wx_object(),
+ run :: wx:wx_object(),
+ stop :: wx:wx_object(),
+ frame :: wx:wx_object(),
+ warnings_box :: wx:wx_object(),
+ explanation_box :: wx:wx_object(),
wantedWarnings :: list(),
rawWarnings :: list(),
backend_pid :: pid(),
@@ -824,7 +823,7 @@ build_analysis_record(#gui_state{mode = Mode, menu = Menu, options = Options,
1 -> src_code
end,
InitPlt =
- case wxMenu:isChecked(Menu#menu.plt,?menuID_PLT_INIT_EMPTY) of
+ case wxMenu:isChecked(Menu#menu.plt, ?menuID_PLT_INIT_EMPTY) of
true -> dialyzer_plt:new();
false -> InitPlt0
end,
diff --git a/lib/dialyzer/test/small_SUITE_data/src/maybe_servers.erl b/lib/dialyzer/test/small_SUITE_data/src/maybe_servers.erl
new file mode 100644
index 0000000000..237f43b1a6
--- /dev/null
+++ b/lib/dialyzer/test/small_SUITE_data/src/maybe_servers.erl
@@ -0,0 +1,31 @@
+-module(maybe_servers).
+
+-export([maybe_server/2, mirror_maybe_server/2]).
+
+maybe_server(O, I) ->
+ case O of
+ no ->
+ maybe_loop(fun(_) -> fin end, I);
+ yes ->
+ maybe_loop(fun(X) -> {ok, X} end, I)
+ end.
+
+maybe_loop(F, X)->
+ case F(X) of
+ {ok, Y} -> maybe_loop(F, Y);
+ fin -> exit(n)
+ end.
+
+mirror_maybe_loop(F, X)->
+ case F(X) of
+ {ok, Y} -> mirror_maybe_loop(F, Y);
+ fin -> exit(n)
+ end.
+
+mirror_maybe_server(O, I) ->
+ case O of
+ no ->
+ mirror_maybe_loop(fun(_) -> fin end, I);
+ yes ->
+ mirror_maybe_loop(fun(X) -> {ok, X} end, I)
+ end.
diff --git a/lib/dialyzer/vsn.mk b/lib/dialyzer/vsn.mk
index 622e51b859..edafcc4afb 100644
--- a/lib/dialyzer/vsn.mk
+++ b/lib/dialyzer/vsn.mk
@@ -1 +1 @@
-DIALYZER_VSN = 2.5
+DIALYZER_VSN = 2.5.1
diff --git a/lib/mnesia/src/mnesia_loader.erl b/lib/mnesia/src/mnesia_loader.erl
index e443b54016..4ba400fbbf 100644
--- a/lib/mnesia/src/mnesia_loader.erl
+++ b/lib/mnesia/src/mnesia_loader.erl
@@ -57,7 +57,7 @@ disc_load_table(Tab, Reason) ->
do_get_disc_copy2(Tab, _Reason, Storage, _Type) when Storage == unknown ->
verbose("Local table copy of ~p has recently been deleted, ignored.~n",
[Tab]),
- {loaded, ok}; %% ?
+ {not_loaded, storage_unknown};
do_get_disc_copy2(Tab, Reason, Storage, Type) when Storage == disc_copies ->
%% NOW we create the actual table
Repair = mnesia_monitor:get_env(auto_repair),
diff --git a/lib/runtime_tools/test/Makefile b/lib/runtime_tools/test/Makefile
index cfaf420d65..3e50ba02ca 100644
--- a/lib/runtime_tools/test/Makefile
+++ b/lib/runtime_tools/test/Makefile
@@ -3,6 +3,7 @@ include $(ERL_TOP)/make/target.mk
include $(ERL_TOP)/make/$(TARGET)/otp.mk
MODULES = \
+ dyntrace_SUITE \
runtime_tools_SUITE \
inviso_testmodule1_foo \
inviso_SUITE \
diff --git a/lib/runtime_tools/test/dyntrace_SUITE.erl b/lib/runtime_tools/test/dyntrace_SUITE.erl
new file mode 100644
index 0000000000..0e4f369ed0
--- /dev/null
+++ b/lib/runtime_tools/test/dyntrace_SUITE.erl
@@ -0,0 +1,224 @@
+%%
+%% %CopyrightBegin%
+%%
+%% Copyright Ericsson AB 2012. All Rights Reserved.
+%%
+%% The contents of this file are subject to the Erlang Public License,
+%% Version 1.1, (the "License"); you may not use this file except in
+%% compliance with the License. You should have received a copy of the
+%% Erlang Public License along with this software. If not, it can be
+%% retrieved online at http://www.erlang.org/.
+%%
+%% Software distributed under the License is distributed on an "AS IS"
+%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
+%% the License for the specific language governing rights and limitations
+%% under the License.
+%%
+%% %CopyrightEnd%
+%%
+-module(dyntrace_SUITE).
+-include_lib("test_server/include/test_server.hrl").
+
+-export([all/0, suite/0,groups/0,init_per_suite/1, end_per_suite/1,
+ init_per_group/2,end_per_group/2]).
+-export([init_per_testcase/2, end_per_testcase/2]).
+
+%% Test cases
+-export([smoke/1,process/1]).
+
+%% Default timetrap timeout (set in init_per_testcase)
+-define(default_timeout, ?t:minutes(1)).
+
+init_per_testcase(_Case, Config) ->
+ Dog = test_server:timetrap(?default_timeout),
+ [{watchdog,Dog}|Config].
+
+end_per_testcase(_Case, Config) ->
+ Dog = ?config(watchdog, Config),
+ ?t:timetrap_cancel(Dog),
+ ok.
+
+suite() -> [{ct_hooks,[ts_install_cth]}].
+
+all() ->
+ case erlang:system_info(dynamic_trace) of
+ none ->
+ {skip,"No dynamic trace in this run-time system"};
+ dtrace ->
+ [{group,smoke}];
+ systemtap ->
+ {skip,"SystemTap tests currently not supported"}
+ end.
+
+groups() ->
+ [{smoke,[sequence],[smoke,{group,rest}]},
+ {rest,[],
+ [process]}].
+
+init_per_suite(Config) ->
+ N = "beam" ++
+ case erlang:system_info(debug_compiled) of
+ false -> "";
+ true -> ".debug"
+ end ++
+ case erlang:system_info(smp_support) of
+ false -> "";
+ true -> ".smp"
+ end,
+ [{emu_name,N}|Config].
+
+end_per_suite(_Config) ->
+ ok.
+
+init_per_group(_GroupName, Config) ->
+ Config.
+
+end_per_group(_GroupName, Config) ->
+ Config.
+
+smoke(Config) ->
+ Emu = ?t:lookup_config(emu_name, Config),
+ BinEmu = list_to_binary(Emu),
+ case erlang:system_info(dynamic_trace) of
+ dtrace ->
+ Probes = os:cmd("sudo /usr/sbin/dtrace -l -m" ++ Emu),
+ io:put_chars(Probes),
+ [_|Lines] = re:split(Probes, "\n", [trim]),
+ [{_,_} = binary:match(L, BinEmu) || L <- Lines],
+ ok
+ end,
+
+ %% Test that the framework for running dtrace/systemtap works
+ %% by executing an empty script.
+ {ok,[]} = dyntrace("", fun() -> ok end),
+ ok.
+
+
+process(_Config) ->
+ Script = [{probe,"process-spawn"},
+ {action,[{printf,["spawn %s %s\n",{arg,0},{arg,1}]}]},
+ {probe,"process-scheduled"},
+ {action,[{printf,["in %s\n",{arg,0}]}]},
+ {probe,"process-unscheduled"},
+ {action,[{printf,["out %s\n",{arg,0}]}]},
+ {probe,"process-hibernate"},
+ {action,[{printf,["hibernate %s %s\n",{arg,0},{arg,1}]}]},
+ {probe,"process-exit"},
+ {action,[{printf,["exit %s %s\n",{arg,0},{arg,1}]}]}
+ ],
+ F = fun() ->
+ {Pid,Ref} = spawn_monitor(fun my_process/0),
+ Pid ! hibernate,
+ Pid ! quit,
+ receive
+ {'DOWN',Ref,process,Pid,{terminated,Pid}} ->
+ ok
+ end,
+ Pid
+ end,
+ {Pid,Output0} = dyntrace(Script, F),
+ Output1 = [termify_line(L) || L <- Output0],
+ PidStr = pid_to_list(Pid),
+ Output = [L || L <- Output1, element(2, L) =:= PidStr],
+ Reason = "{terminated,"++PidStr++"}",
+ io:format("~p\n", [Output]),
+ [{spawn,PidStr,"erlang:apply/2"},
+ {in,PidStr},
+ {hibernate,PidStr,"erlang:apply/2"},
+ {out,PidStr},
+ {in,PidStr},
+ {exit,PidStr,Reason},
+ {out,PidStr}] = Output,
+ ok.
+
+termify_line(L) ->
+ [H|T] = re:split(L, " ", [{return,list}]),
+ list_to_tuple([list_to_atom(H)|T]).
+
+my_process() ->
+ receive
+ hibernate ->
+ erlang:hibernate(erlang, apply, [fun my_process/0,[]]);
+ quit ->
+ exit({terminated,self()})
+ end.
+
+%%%
+%%% Utility functions.
+%%%
+
+dyntrace(Script0, Action) ->
+ Sudo = os:find_executable(sudo),
+ {Termination,Pid} = termination_probe(),
+ Script1 = Script0++Termination,
+ Script = translate_script(Script1),
+ io:format("~s\n", [Script]),
+ SrcFile = "test-dyntrace.d",
+ ok = file:write_file(SrcFile, Script),
+ Args = ["/usr/sbin/dtrace", "-q","-s",SrcFile],
+ Port = open_port({spawn_executable,Sudo},
+ [{args,Args},stream,in,stderr_to_stdout,eof]),
+ receive
+ {Port,{data,Sofar}} ->
+ Res = Action(),
+ Pid ! quit,
+ {Res,get_data(Port, Sofar)}
+ end.
+
+get_data(Port, Sofar) ->
+ receive
+ {Port,{data,Bytes}} ->
+ get_data(Port, [Sofar|Bytes]);
+ {Port,eof} ->
+ port_close(Port),
+ [$\n|T] = lists:flatten(Sofar),
+ re:split(T, "\n", [{return,list},trim])
+ end.
+
+termination_probe() ->
+ Pid = spawn(fun() ->
+ receive
+ _ ->
+ exit(done)
+ end
+ end),
+ S = [{'BEGIN',[{printf,["\n"]}]},
+ {probe,"process-exit"},
+ {pred,{'==',{arg,0},Pid}},
+ {action,[{exit,[0]}]}],
+ {S,Pid}.
+
+translate_script(Script) ->
+ [dtrace_op(Op) || Op <- Script].
+
+dtrace_op({probe,Function}) ->
+ OsPid = os:getpid(),
+ ["erlang",OsPid,":::",Function,$\n];
+dtrace_op({pred,Pred}) ->
+ ["/",dtrace_op(Pred),"/\n"];
+dtrace_op({action,List}) ->
+ ["{ ",action_list(List)," }\n\n"];
+dtrace_op({'BEGIN',List}) ->
+ ["BEGIN { ",action_list(List)," }\n\n"];
+dtrace_op({'==',Op1,Op2}) ->
+ [dtrace_op(Op1)," == ",dtrace_op(Op2)];
+dtrace_op({arg,N}) ->
+ ["copyinstr(arg",integer_to_list(N),")"];
+dtrace_op({Func,List}) when is_atom(Func), is_list(List) ->
+ [atom_to_list(Func),"(",comma_sep_ops(List),")"];
+dtrace_op(Pid) when is_pid(Pid) ->
+ ["\"",pid_to_list(Pid),"\""];
+dtrace_op(Str) when is_integer(hd(Str)) ->
+ io_lib:format("~p", [Str]);
+dtrace_op(Int) when is_integer(Int) ->
+ integer_to_list(Int).
+
+comma_sep_ops([A,B|T]) ->
+ [dtrace_op(A),","|comma_sep_ops([B|T])];
+comma_sep_ops([H]) ->
+ dtrace_op(H);
+comma_sep_ops([]) -> [].
+
+action_list([H|T]) ->
+ [dtrace_op(H),";"|action_list(T)];
+action_list([]) -> [].
diff --git a/lib/ssl/doc/src/ssl.xml b/lib/ssl/doc/src/ssl.xml
index 4910a6f1b8..62a79e15eb 100644
--- a/lib/ssl/doc/src/ssl.xml
+++ b/lib/ssl/doc/src/ssl.xml
@@ -122,6 +122,9 @@
<p> <c>hash() = md5 | sha
</c></p>
+ <p><c>prf_random() = client_random | server_random
+ </c></p>
+
</section>
<section>
@@ -561,6 +564,26 @@ fun(OtpCert :: #'OTPCertificate'{}, Event :: {bad_cert, Reason :: atom()} |
</func>
<func>
+ <name>prf(Socket, Secret, Label, Seed, WantedLength) -> {ok, binary()} | {error, reason()}</name>
+ <fsummary>Use a sessions pseudo random function to generate key material.</fsummary>
+ <type>
+ <v>Socket = sslsocket()</v>
+ <v>Secret = binary() | master_secret</v>
+ <v>Label = binary()</v>
+ <v>Seed = [binary() | prf_random()]</v>
+ <v>WantedLength = non_neg_integer()</v>
+ </type>
+ <desc>
+ <p>Use the pseudo random function (PRF) of a TLS session to generate
+ additional key material. It either takes user generated values for
+ <c>Secret</c> and <c>Seed</c> or atoms directing it use a specific
+ value from the session security parameters.</p>
+ <p>This function can only be used with TLS connections, <c>{error, undefined}</c>
+ is returned for SSLv3 connections.</p>
+ </desc>
+ </func>
+
+ <func>
<name>renegotiate(Socket) -> ok | {error, Reason}</name>
<fsummary> Initiates a new handshake.</fsummary>
<type>
diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl
index d0693445e0..1048583eca 100644
--- a/lib/ssl/src/ssl.erl
+++ b/lib/ssl/src/ssl.erl
@@ -30,7 +30,7 @@
controlling_process/2, listen/2, pid/1, peername/1, peercert/1,
recv/2, recv/3, send/2, getopts/2, setopts/2, sockname/1,
versions/0, session_info/1, format_error/1,
- renegotiate/1]).
+ renegotiate/1, prf/5]).
-deprecated({pid, 1, next_major_release}).
@@ -67,7 +67,7 @@
-type ssl_imp() :: new | old.
-type transport_option() :: {cb_info, {CallbackModule::atom(), DataTag::atom(), ClosedTag::atom()}}.
-
+-type prf_random() :: client_random | server_random.
%%--------------------------------------------------------------------
-spec start() -> ok | {error, reason()}.
@@ -414,6 +414,17 @@ versions() ->
renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) ->
ssl_connection:renegotiation(Pid).
+%%--------------------------------------------------------------------
+-spec prf(#sslsocket{}, binary() | 'master_secret', binary(),
+ binary() | prf_random(), non_neg_integer()) ->
+ {ok, binary()} | {error, reason()}.
+%%
+%% Description: use a ssl sessions TLS PRF to generate key material
+%%--------------------------------------------------------------------
+prf(#sslsocket{pid = Pid, fd = new_ssl},
+ Secret, Label, Seed, WantedLength) ->
+ ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength).
+
%%---------------------------------------------------------------
-spec format_error({error, term()}) -> list().
%%
diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl
index 28dd0c85d0..59133dccf0 100644
--- a/lib/ssl/src/ssl_connection.erl
+++ b/lib/ssl/src/ssl_connection.erl
@@ -40,7 +40,8 @@
-export([send/2, recv/3, connect/7, ssl_accept/6, handshake/2,
socket_control/3, close/1, shutdown/2,
new_user/2, get_opts/2, set_opts/2, info/1, session_info/1,
- peer_certificate/1, sockname/1, peername/1, renegotiation/1]).
+ peer_certificate/1, sockname/1, peername/1, renegotiation/1,
+ prf/5]).
%% Called by ssl_connection_sup
-export([start_link/7]).
@@ -273,6 +274,16 @@ peer_certificate(ConnectionPid) ->
renegotiation(ConnectionPid) ->
sync_send_all_state_event(ConnectionPid, renegotiate).
+%%--------------------------------------------------------------------
+-spec prf(pid(), binary() | 'master_secret', binary(),
+ binary() | ssl:prf_secret(), non_neg_integer()) ->
+ {ok, binary()} | {error, reason()} | {'EXIT', term()}.
+%%
+%% Description: use a ssl sessions TLS PRF to generate key material
+%%--------------------------------------------------------------------
+prf(ConnectionPid, Secret, Label, Seed, WantedLength) ->
+ sync_send_all_state_event(ConnectionPid, {prf, Secret, Label, Seed, WantedLength}).
+
%%====================================================================
%% ssl_connection_sup API
%%====================================================================
@@ -868,6 +879,32 @@ handle_sync_event(renegotiate, From, connection, State) ->
handle_sync_event(renegotiate, _, StateName, State) ->
{reply, {error, already_renegotiating}, StateName, State, get_timeout(State)};
+handle_sync_event({prf, Secret, Label, Seed, WantedLength}, _, StateName,
+ #state{connection_states = ConnectionStates,
+ negotiated_version = Version} = State) ->
+ ConnectionState =
+ ssl_record:current_connection_state(ConnectionStates, read),
+ SecParams = ConnectionState#connection_state.security_parameters,
+ #security_parameters{master_secret = MasterSecret,
+ client_random = ClientRandom,
+ server_random = ServerRandom} = SecParams,
+ Reply = try
+ SecretToUse = case Secret of
+ _ when is_binary(Secret) -> Secret;
+ master_secret -> MasterSecret
+ end,
+ SeedToUse = lists:reverse(
+ lists:foldl(fun(X, Acc) when is_binary(X) -> [X|Acc];
+ (client_random, Acc) -> [ClientRandom|Acc];
+ (server_random, Acc) -> [ServerRandom|Acc]
+ end, [], Seed)),
+ ssl_handshake:prf(Version, SecretToUse, Label, SeedToUse, WantedLength)
+ catch
+ exit:_ -> {error, badarg};
+ error:Reason -> {error, Reason}
+ end,
+ {reply, Reply, StateName, State, get_timeout(State)};
+
handle_sync_event(info, _, StateName,
#state{negotiated_version = Version,
session = #session{cipher_suite = Suite}} = State) ->
diff --git a/lib/ssl/src/ssl_handshake.erl b/lib/ssl/src/ssl_handshake.erl
index 542033e6ce..ef60cac6df 100644
--- a/lib/ssl/src/ssl_handshake.erl
+++ b/lib/ssl/src/ssl_handshake.erl
@@ -37,7 +37,7 @@
finished/4, verify_connection/5, get_tls_handshake/2,
decode_client_key/3, server_hello_done/0,
encode_handshake/2, init_hashes/0, update_hashes/2,
- decrypt_premaster_secret/2]).
+ decrypt_premaster_secret/2, prf/5]).
-export([dec_hello_extensions/2]).
@@ -543,6 +543,18 @@ server_key_exchange_hash(dhe_dss, Value) ->
crypto:sha(Value).
%%--------------------------------------------------------------------
+-spec prf(tls_version(), binary(), binary(), binary(), non_neg_integer()) ->
+ {ok, binary()} | {error, undefined}.
+%%
+%% Description: use the TLS PRF to generate key material
+%%--------------------------------------------------------------------
+prf({3,0}, _, _, _, _) ->
+ {error, undefined};
+prf({3,N}, Secret, Label, Seed, WantedLength)
+ when N == 1; N == 2 ->
+ {ok, ssl_tls1:prf(Secret, Label, Seed, WantedLength)}.
+
+%%--------------------------------------------------------------------
%%% Internal functions
%%--------------------------------------------------------------------
get_tls_handshake_aux(<<?BYTE(Type), ?UINT24(Length),
diff --git a/lib/ssl/src/ssl_tls1.erl b/lib/ssl/src/ssl_tls1.erl
index 5f9850c386..7351f34b61 100644
--- a/lib/ssl/src/ssl_tls1.erl
+++ b/lib/ssl/src/ssl_tls1.erl
@@ -29,7 +29,7 @@
-include("ssl_record.hrl").
-export([master_secret/3, finished/3, certificate_verify/2, mac_hash/7,
- setup_keys/6, suites/0]).
+ setup_keys/6, suites/0, prf/4]).
%%====================================================================
%% Internal application API