diff options
Diffstat (limited to 'lib')
27 files changed, 659 insertions, 176 deletions
diff --git a/lib/kernel/src/disk_log.erl b/lib/kernel/src/disk_log.erl index 5b1efcd395..1513fdaec0 100644 --- a/lib/kernel/src/disk_log.erl +++ b/lib/kernel/src/disk_log.erl @@ -44,6 +44,8 @@ %% To be used for debugging only: -export([pid2name/1]). +-export_type([continuation/0]). + -type dlog_state_error() :: 'ok' | {'error', term()}. -record(state, {queue = [], diff --git a/lib/kernel/src/pg2.erl b/lib/kernel/src/pg2.erl index 0d5838716e..1ff10eb303 100644 --- a/lib/kernel/src/pg2.erl +++ b/lib/kernel/src/pg2.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -159,7 +159,7 @@ get_closest_pid(Name) -> -record(state, {}). --opaque state() :: #state{}. +-type state() :: #state{}. -spec init(Arg :: []) -> {'ok', state()}. diff --git a/lib/kernel/src/rpc.erl b/lib/kernel/src/rpc.erl index 0b1fc6e939..7c965ca384 100644 --- a/lib/kernel/src/rpc.erl +++ b/lib/kernel/src/rpc.erl @@ -62,6 +62,8 @@ %% Internals -export([proxy_user_flush/0]). +-export_type([key/0]). + %%------------------------------------------------------------------------ -type state() :: gb_tree(). diff --git a/lib/kernel/src/wrap_log_reader.erl b/lib/kernel/src/wrap_log_reader.erl index c41e0091e4..689269fc28 100644 --- a/lib/kernel/src/wrap_log_reader.erl +++ b/lib/kernel/src/wrap_log_reader.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1998-2011. All Rights Reserved. +%% Copyright Ericsson AB 1998-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 @@ -30,6 +30,8 @@ -export([open/1, open/2, chunk/1, chunk/2, close/1]). +-export_type([continuation/0]). + -include("disk_log.hrl"). -record(wrap_reader, diff --git a/lib/ssh/doc/src/ssh_connection.xml b/lib/ssh/doc/src/ssh_connection.xml index 9942306b93..a9ae13d556 100644 --- a/lib/ssh/doc/src/ssh_connection.xml +++ b/lib/ssh/doc/src/ssh_connection.xml @@ -196,7 +196,7 @@ <name>send(ConnectionRef, ChannelId, Data, Timeout) -></name> <name>send(ConnectionRef, ChannelId, Type, Data) -></name> <name>send(ConnectionRef, ChannelId, Type, Data, TimeOut) -> - ok | {error, timeout}</name> + ok | {error, timeout} | {error, closed}</name> <fsummary>Sends channel data </fsummary> <type> <v> ConnectionRef = ssh_connection_ref() </v> @@ -212,7 +212,7 @@ </func> <func> - <name>send_eof(ConnectionRef, ChannelId) -> ok </name> + <name>send_eof(ConnectionRef, ChannelId) -> ok | {error, closed}</name> <fsummary>Sends eof on the channel <c>ChannelId</c>. </fsummary> <type> <v> ConnectionRef = ssh_connection_ref() </v> diff --git a/lib/ssh/src/ssh_cli.erl b/lib/ssh/src/ssh_cli.erl index 781e01b9d1..c8c610f8ef 100644 --- a/lib/ssh/src/ssh_cli.erl +++ b/lib/ssh/src/ssh_cli.erl @@ -81,7 +81,8 @@ handle_ssh_msg({ssh_cm, ConnectionManager, height = not_zero(Height, 24), pixel_width = PixWidth, pixel_height = PixHeight, - modes = Modes}}, + modes = Modes}, + buf = empty_buf()}, set_echo(State), ssh_connection:reply_request(ConnectionManager, WantReply, success, ChannelId), diff --git a/lib/ssh/src/ssh_connection.erl b/lib/ssh/src/ssh_connection.erl index e3b8ebfb79..9424cdd423 100644 --- a/lib/ssh/src/ssh_connection.erl +++ b/lib/ssh/src/ssh_connection.erl @@ -177,7 +177,7 @@ close(ConnectionManager, ChannelId) -> %% Description: Send status replies to requests that want such replies. %%-------------------------------------------------------------------- reply_request(ConnectionManager, true, Status, ChannelId) -> - ConnectionManager ! {ssh_cm, self(), {Status, ChannelId}}, + ssh_connection_manager:reply_request(ConnectionManager, Status, ChannelId), ok; reply_request(_,false, _, _) -> ok. @@ -318,21 +318,22 @@ channel_data(ChannelId, DataType, Data, From) -> case ssh_channel:cache_lookup(Cache, ChannelId) of - #channel{remote_id = Id} = Channel0 -> - {SendList, Channel} = update_send_window(Channel0, DataType, + #channel{remote_id = Id, sent_close = false} = Channel0 -> + {SendList, Channel} = update_send_window(Channel0#channel{flow_control = From}, DataType, Data, Connection), Replies = lists:map(fun({SendDataType, SendData}) -> - {connection_reply, ConnectionPid, - channel_data_msg(Id, - SendDataType, - SendData)} + {connection_reply, ConnectionPid, + channel_data_msg(Id, + SendDataType, + SendData)} end, SendList), FlowCtrlMsgs = flow_control(Replies, - Channel#channel{flow_control = From}, + Channel, Cache), {{replies, Replies ++ FlowCtrlMsgs}, Connection}; - undefined -> + _ -> + gen_server:reply(From, {error, closed}), {noreply, Connection} end. @@ -386,20 +387,30 @@ handle_msg(#ssh_msg_channel_close{recipient_channel = ChannelId}, ConnectionPid, _) -> case ssh_channel:cache_lookup(Cache, ChannelId) of - #channel{sent_close = Closed, remote_id = RemoteId} = Channel -> + #channel{sent_close = Closed, remote_id = RemoteId, flow_control = FlowControl} = Channel -> ssh_channel:cache_delete(Cache, ChannelId), {CloseMsg, Connection} = reply_msg(Channel, Connection0, {closed, ChannelId}), + + ConnReplyMsgs = case Closed of - true -> - {{replies, [CloseMsg]}, Connection}; + true -> []; false -> RemoteCloseMsg = channel_close_msg(RemoteId), - {{replies, - [{connection_reply, - ConnectionPid, RemoteCloseMsg}, - CloseMsg]}, Connection} - end; + [{connection_reply, ConnectionPid, RemoteCloseMsg}] + end, + + %% if there was a send() in progress, make it fail + SendReplyMsgs = + case FlowControl of + undefined -> []; + From -> + [{flow_control, From, {error, closed}}] + end, + + Replies = ConnReplyMsgs ++ [CloseMsg] ++ SendReplyMsgs, + {{replies, Replies}, Connection}; + undefined -> {{replies, []}, Connection0} end; @@ -1126,13 +1137,13 @@ flow_control(Channel, Cache) -> flow_control([], Channel, Cache) -> ssh_channel:cache_update(Cache, Channel), []; -flow_control([_|_], #channel{flow_control = From} = Channel, Cache) -> - case From of - undefined -> - []; - _ -> - [{flow_control, Cache, Channel, From, ok}] - end. + +flow_control([_|_], #channel{flow_control = From, + send_buf = []} = Channel, Cache) when From =/= undefined -> + [{flow_control, Cache, Channel, From, ok}]; +flow_control(_,_,_) -> + []. + encode_pty_opts(Opts) -> Bin = list_to_binary(encode_pty_opts2(Opts)), diff --git a/lib/ssh/src/ssh_connection_manager.erl b/lib/ssh/src/ssh_connection_manager.erl index e53cd4f4f7..422d9356d5 100644 --- a/lib/ssh/src/ssh_connection_manager.erl +++ b/lib/ssh/src/ssh_connection_manager.erl @@ -40,7 +40,7 @@ close/2, stop/1, send/5, send_eof/2]). --export([open_channel/6, request/6, request/7, global_request/4, event/2, +-export([open_channel/6, reply_request/3, request/6, request/7, global_request/4, event/2, cast/2]). %% Internal application API and spawn @@ -95,6 +95,9 @@ request(ConnectionManager, ChannelId, Type, true, Data, Timeout) -> request(ConnectionManager, ChannelId, Type, false, Data, _) -> cast(ConnectionManager, {request, ChannelId, Type, Data}). +reply_request(ConnectionManager, Status, ChannelId) -> + cast(ConnectionManager, {reply_request, Status, ChannelId}). + global_request(ConnectionManager, Type, true = Reply, Data) -> case call(ConnectionManager, {global_request, self(), Type, Reply, Data}) of @@ -163,7 +166,7 @@ send(ConnectionManager, ChannelId, Type, Data, Timeout) -> call(ConnectionManager, {data, ChannelId, Type, Data}, Timeout). send_eof(ConnectionManager, ChannelId) -> - cast(ConnectionManager, {eof, ChannelId}). + call(ConnectionManager, {eof, ChannelId}). %%==================================================================== %% gen_server callbacks @@ -295,6 +298,18 @@ handle_call({data, ChannelId, Type, Data}, From, channel_data(ChannelId, Type, Data, Connection0, ConnectionPid, From, State); +handle_call({eof, ChannelId}, _From, + #state{connection = Pid, connection_state = + #connection{channel_cache = Cache}} = State) -> + case ssh_channel:cache_lookup(Cache, ChannelId) of + #channel{remote_id = Id, sent_close = false} -> + send_msg({connection_reply, Pid, + ssh_connection:channel_eof_msg(Id)}), + {reply, ok, State}; + _ -> + {reply, {error,closed}, State} + end; + handle_call({connection_info, Options}, From, #state{connection = Connection} = State) -> ssh_connection_handler:connection_info(Connection, From, Options), @@ -431,6 +446,16 @@ handle_cast({request, ChannelId, Type, Data}, State0) -> lists:foreach(fun send_msg/1, Replies), {noreply, State}; +handle_cast({reply_request, Status, ChannelId}, #state{connection_state = + #connection{channel_cache = Cache}} = State0) -> + State = case ssh_channel:cache_lookup(Cache, ChannelId) of + #channel{remote_id = RemoteId} -> + cm_message({Status, RemoteId}, State0); + undefined -> + State0 + end, + {noreply, State}; + handle_cast({global_request, _, _, _, _} = Request, State0) -> State = handle_global_request(Request, State0), {noreply, State}; @@ -453,18 +478,6 @@ handle_cast({adjust_window, ChannelId, Bytes}, end, {noreply, State}; -handle_cast({eof, ChannelId}, - #state{connection = Pid, connection_state = - #connection{channel_cache = Cache}} = State) -> - case ssh_channel:cache_lookup(Cache, ChannelId) of - #channel{remote_id = Id} -> - send_msg({connection_reply, Pid, - ssh_connection:channel_eof_msg(Id)}), - {noreply, State}; - undefined -> - {noreply, State} - end; - handle_cast({success, ChannelId}, #state{connection = Pid} = State) -> Msg = ssh_connection:channel_success_msg(ChannelId), send_msg({connection_reply, Pid, Msg}), @@ -614,6 +627,8 @@ do_send_msg({connection_reply, Pid, Data}) -> ssh_connection_handler:send(Pid, Msg); do_send_msg({flow_control, Cache, Channel, From, Msg}) -> ssh_channel:cache_update(Cache, Channel#channel{flow_control = undefined}), + gen_server:reply(From, Msg); +do_send_msg({flow_control, From, Msg}) -> gen_server:reply(From, Msg). handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From, diff --git a/lib/ssh/test/Makefile b/lib/ssh/test/Makefile index 25072688ad..f5db31baee 100644 --- a/lib/ssh/test/Makefile +++ b/lib/ssh/test/Makefile @@ -36,7 +36,9 @@ MODULES= \ ssh_to_openssh_SUITE \ ssh_sftp_SUITE \ ssh_sftpd_SUITE \ - ssh_sftpd_erlclient_SUITE + ssh_sftpd_erlclient_SUITE \ + ssh_connection_SUITE \ + ssh_echo_server HRL_FILES_NEEDED_IN_TEST= \ $(ERL_TOP)/lib/ssh/src/ssh.hrl \ diff --git a/lib/ssh/test/ssh_connection_SUITE.erl b/lib/ssh/test/ssh_connection_SUITE.erl new file mode 100644 index 0000000000..43a899f974 --- /dev/null +++ b/lib/ssh/test/ssh_connection_SUITE.erl @@ -0,0 +1,312 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2008-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(ssh_connection_SUITE). + +-include_lib("common_test/include/ct.hrl"). + +-compile(export_all). + +-define(SSH_DEFAULT_PORT, 22). +-define(EXEC_TIMEOUT, 10000). + +%%-------------------------------------------------------------------- +suite() -> + [{ct_hooks,[ts_install_cth]}]. + +all() -> + [ + {group, erlang_client}, + interrupted_send + ]. +groups() -> + [{erlang_client, [], [simple_exec, + small_cat, + big_cat, + send_after_exit + ]}]. + +%%-------------------------------------------------------------------- + +init_per_suite(Config) -> + case catch crypto:start() of + ok -> + Config; + _Else -> + {skip, "Crypto could not be started!"} + end. + +end_per_suite(_Config) -> + crypto:stop(), + ok. +%%-------------------------------------------------------------------- +init_per_group(erlang_client, Config) -> + case gen_tcp:connect("localhost", 22, []) of + {error,econnrefused} -> + {skip,"No openssh deamon"}; + _ -> + Config + end; +init_per_group(_, Config) -> + Config. + +end_per_group(_, Config) -> + Config. + +%%-------------------------------------------------------------------- +init_per_testcase(_TestCase, Config) -> + ssh:start(), + Config. + +end_per_testcase(_Config) -> + ssh:stop(), + ok. + +%%% TEST cases starts here. +%%-------------------------------------------------------------------- +simple_exec(doc) -> + ["Simple openssh connectivity test for ssh_connection:exec"]; + +simple_exec(Config) when is_list(Config) -> + ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, + {user_interaction, false}]), + {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:exec(ConnectionRef, ChannelId0, + "echo testing", infinity), + + %% receive response to input + receive + {ssh_cm, ConnectionRef, {data, ChannelId0, 0, <<"testing\n">>}} -> + ok + end, + + %% receive close messages + receive + {ssh_cm, ConnectionRef, {eof, ChannelId0}} -> + ok + end, + receive + {ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}} -> + ok + end, + receive + {ssh_cm, ConnectionRef,{closed, ChannelId0}} -> + ok + end. + +%%-------------------------------------------------------------------- +small_cat(doc) -> + ["Use 'cat' to echo small data block back to us."]; + +small_cat(Config) when is_list(Config) -> + ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, + {user_interaction, false}]), + {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:exec(ConnectionRef, ChannelId0, + "cat", infinity), + + Data = <<"I like spaghetti squash">>, + ok = ssh_connection:send(ConnectionRef, ChannelId0, Data), + ok = ssh_connection:send_eof(ConnectionRef, ChannelId0), + + %% receive response to input + receive + {ssh_cm, ConnectionRef, {data, ChannelId0, 0, Data}} -> + ok + end, + + %% receive close messages + receive + {ssh_cm, ConnectionRef, {eof, ChannelId0}} -> + ok + end, + receive + {ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}} -> + ok + end, + receive + {ssh_cm, ConnectionRef,{closed, ChannelId0}} -> + ok + end. + +%%-------------------------------------------------------------------- +big_cat(doc) -> + ["Use 'cat' to echo large data block back to us."]; + +big_cat(Config) when is_list(Config) -> + ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, + {user_interaction, false}]), + {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity), + success = ssh_connection:exec(ConnectionRef, ChannelId0, + "cat", infinity), + + %% build 10MB binary + Data = << <<X:32>> || X <- lists:seq(1,2500000)>>, + + %% pre-adjust receive window so the other end doesn't block + ssh_connection:adjust_window(ConnectionRef, ChannelId0, size(Data)), + + test_server:format("sending ~p byte binary~n",[size(Data)]), + ok = ssh_connection:send(ConnectionRef, ChannelId0, Data, 10000), + ok = ssh_connection:send_eof(ConnectionRef, ChannelId0), + + %% collect echoed data until eof + case big_cat_rx(ConnectionRef, ChannelId0) of + {ok, Data} -> + ok; + {ok, Other} -> + case size(Data) =:= size(Other) of + true -> + test_server:format("received and sent data are same" + "size but do not match~n",[]); + false -> + test_server:format("sent ~p but only received ~p~n", + [size(Data), size(Other)]) + end, + ct:fail(receive_data_mismatch); + Else -> + ct:fail(Else) + end, + + %% receive close messages (eof already consumed) + receive + {ssh_cm, ConnectionRef, {exit_status, ChannelId0, 0}} -> + ok + end, + receive + {ssh_cm, ConnectionRef,{closed, ChannelId0}} -> + ok + end. + +big_cat_rx(ConnectionRef, ChannelId) -> + big_cat_rx(ConnectionRef, ChannelId, []). + +big_cat_rx(ConnectionRef, ChannelId, Acc) -> + receive + {ssh_cm, ConnectionRef, {data, ChannelId, 0, Data}} -> + %% ssh_connection:adjust_window(ConnectionRef, ChannelId, size(Data)), + %% window was pre-adjusted, don't adjust again here + big_cat_rx(ConnectionRef, ChannelId, [Data | Acc]); + {ssh_cm, ConnectionRef, {eof, ChannelId}} -> + {ok, iolist_to_binary(lists:reverse(Acc))} + after ?EXEC_TIMEOUT -> + timeout + end. + +%%-------------------------------------------------------------------- +send_after_exit(doc) -> + ["Send channel data after the channel has been closed."]; + +send_after_exit(Config) when is_list(Config) -> + ConnectionRef = ssh_test_lib:connect(?SSH_DEFAULT_PORT, [{silently_accept_hosts, true}, + {user_interaction, false}]), + {ok, ChannelId0} = ssh_connection:session_channel(ConnectionRef, infinity), + + %% Shell command "false" will exit immediately + success = ssh_connection:exec(ConnectionRef, ChannelId0, + "false", infinity), + + timer:sleep(2000), %% Allow incoming eof/close/exit_status ssh messages to be processed + + Data = <<"I like spaghetti squash">>, + case ssh_connection:send(ConnectionRef, ChannelId0, Data, 2000) of + {error, closed} -> ok; + ok -> + ct:fail({expected,{error,closed}}); + {error, timeout} -> + ct:fail({expected,{error,closed}}); + Else -> + ct:fail(Else) + end, + + %% receive close messages + receive + {ssh_cm, ConnectionRef, {eof, ChannelId0}} -> + ok + end, + receive + {ssh_cm, ConnectionRef, {exit_status, ChannelId0, _}} -> + ok + end, + receive + {ssh_cm, ConnectionRef,{closed, ChannelId0}} -> + ok + end. +%%-------------------------------------------------------------------- +interrupted_send(doc) -> + ["Use a subsystem that echos n char and then sends eof to cause a channel exit partway through a large send."]; + +interrupted_send(Config) when is_list(Config) -> + PrivDir = ?config(priv_dir, Config), + UserDir = filename:join(PrivDir, nopubkey), % to make sure we don't use public-key-auth + file:make_dir(UserDir), + SysDir = ?config(data_dir, Config), + {Pid, Host, Port} = ssh_test_lib:daemon([{system_dir, SysDir}, + {user_dir, UserDir}, + {password, "morot"}, + {subsystems, [{"echo_n", {ssh_echo_server, [4000000]}}]}]), + + ConnectionRef = ssh_test_lib:connect(Host, Port, [{silently_accept_hosts, true}, + {user, "foo"}, + {password, "morot"}, + {user_interaction, false}, + {user_dir, UserDir}]), + + {ok, ChannelId} = ssh_connection:session_channel(ConnectionRef, infinity), + + success = ssh_connection:subsystem(ConnectionRef, ChannelId, "echo_n", infinity), + + %% build 10MB binary + Data = << <<X:32>> || X <- lists:seq(1,2500000)>>, + + %% expect remote end to send us 4MB back + <<ExpectedData:4000000/binary, _/binary>> = Data, + + %% pre-adjust receive window so the other end doesn't block + ssh_connection:adjust_window(ConnectionRef, ChannelId, size(ExpectedData) + 1), + + case ssh_connection:send(ConnectionRef, ChannelId, Data, 10000) of + {error, closed} -> + ok; + Msg -> + ct:fail({expected,{error,closed}, got, Msg}) + end, + receive_data(ExpectedData, ConnectionRef, ChannelId), + ssh:close(ConnectionRef), + ssh:stop_daemon(Pid). + + +%% Internal funtions ------------------------------------------------------------------ + +receive_data(ExpectedData, ConnectionRef, ChannelId) -> + ExpectedData = collect_data(ConnectionRef, ChannelId). + +collect_data(ConnectionRef, ChannelId) -> + collect_data(ConnectionRef, ChannelId, []). + +collect_data(ConnectionRef, ChannelId, Acc) -> + receive + {ssh_cm, ConnectionRef, {data, ChannelId, 0, Data}} -> + collect_data(ConnectionRef, ChannelId, [Data | Acc]); + {ssh_cm, ConnectionRef, {eof, ChannelId}} -> + iolist_to_binary(lists:reverse(Acc)) + after 5000 -> + timeout + end. diff --git a/lib/ssh/test/ssh_connection_SUITE_data/ssh_host_rsa_key b/lib/ssh/test/ssh_connection_SUITE_data/ssh_host_rsa_key new file mode 100644 index 0000000000..6ae7ee023d --- /dev/null +++ b/lib/ssh/test/ssh_connection_SUITE_data/ssh_host_rsa_key @@ -0,0 +1,15 @@ +-----BEGIN RSA PRIVATE KEY----- +MIICXQIBAAKBgQDCZX+4FBDwZIh9y/Uxee1VJnEXlowpz2yDKwj8semM4q843337 +zbNfxHmladB1lpz2NqyxI175xMIJuDxogyZdsOxGnFAzAnthR4dqL/RWRWzjaxSB +6IAO9SPYVVlrpZ+1hsjLW79fwXK/yc8VdhRuWTeQiRgYY2ek8+OKbOqz4QIDAQAB +AoGANmvJzJO5hkLuvyDZHKfAnGTtpifcR1wtSa9DjdKUyn8vhKF0mIimnbnYQEmW +NUUb3gXCZLi9PvkpRSVRrASDOZwcjoU/Kvww163vBUVb2cOZfFhyn6o2Sk88Tt++ +udH3hdjpf9i7jTtUkUe+QYPsia+wgvvrmn4QrahLAH86+kECQQDx5gFeXTME3cnW +WMpFz3PPumduzjqgqMMWEccX4FtQkMX/gyGa5UC7OHFyh0N/gSWvPbRHa8A6YgIt +n8DO+fh5AkEAzbqX4DOn8NY6xJIi42q7l/2jIA0RkB6P7YugW5NblhqBZ0XDnpA5 +sMt+rz+K07u9XZtxgh1xi7mNfwY6lEAMqQJBAJBEauCKmRj35Z6OyeQku59SPsnY ++SJEREVvSNw2lH9SOKQQ4wPsYlTGbvKtNVZgAcen91L5MmYfeckYE/fdIZECQQCt +64zxsTnM1I8iFxj/gP/OYlJBikrKt8udWmjaghzvLMEw+T2DExJyb9ZNeT53+UMB +m6O+B/4xzU/djvp+0hbhAkAemIt+rA5kTmYlFndhpvzkSSM8a2EXsO4XIPgGWCTT +tQKS/tTly0ADMjN/TVy11+9d6zcqadNVuHXHGtR4W0GR +-----END RSA PRIVATE KEY----- diff --git a/lib/ssh/test/ssh_echo_server.erl b/lib/ssh/test/ssh_echo_server.erl new file mode 100644 index 0000000000..739aabe6fb --- /dev/null +++ b/lib/ssh/test/ssh_echo_server.erl @@ -0,0 +1,71 @@ +%% +%% %CopyrightBegin% +%% +%% Copyright Ericsson AB 2005-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% +%% + +%% + +%%% Description: Example ssh server +-module(ssh_echo_server). +-behaviour(ssh_channel). +-record(state, { + n, + id, + cm + }). +-export([init/1, handle_msg/2, handle_ssh_msg/2, terminate/2]). + +init([N]) -> + {ok, #state{n = N}}. + +handle_msg({ssh_channel_up, ChannelId, ConnectionManager}, State) -> + {ok, State#state{id = ChannelId, + cm = ConnectionManager}}. + +handle_ssh_msg({ssh_cm, CM, {data, ChannelId, 0, Data}}, #state{n = N} = State) -> + M = N - size(Data), + case M > 0 of + true -> + ssh_connection:send(CM, ChannelId, Data), + {ok, State#state{n = M}}; + false -> + <<SendData:N/binary, _/binary>> = Data, + ssh_connection:send(CM, ChannelId, SendData), + ssh_connection:send_eof(CM, ChannelId), + {stop, ChannelId, State} + end; +handle_ssh_msg({ssh_cm, _ConnectionManager, + {data, _ChannelId, 1, Data}}, State) -> + error_logger:format("ssh: STDERR: ~s\n", [binary_to_list(Data)]), + {ok, State}; + +handle_ssh_msg({ssh_cm, _ConnectionManager, {eof, _ChannelId}}, State) -> + {ok, State}; + +handle_ssh_msg({ssh_cm, _, {signal, _, _}}, State) -> + %% Ignore signals according to RFC 4254 section 6.9. + {ok, State}; + +handle_ssh_msg({ssh_cm, _, {exit_signal, ChannelId, _, _Error, _}}, + State) -> + {stop, ChannelId, State}; + +handle_ssh_msg({ssh_cm, _, {exit_status, ChannelId, _Status}}, State) -> + {stop, ChannelId, State}. + +terminate(_Reason, _State) -> + ok. diff --git a/lib/ssl/src/ssl.erl b/lib/ssl/src/ssl.erl index 9a562aa5a8..7788f758ac 100644 --- a/lib/ssl/src/ssl.erl +++ b/lib/ssl/src/ssl.erl @@ -165,7 +165,7 @@ listen(Port, Options0) -> #config{cb={CbModule, _, _, _},inet_user=Options} = Config, case CbModule:listen(Port, Options) of {ok, ListenSocket} -> - {ok, #sslsocket{pid = {ListenSocket, Config}, fd = new_ssl}}; + {ok, #sslsocket{pid = {ListenSocket, Config}}}; Err = {error, _} -> Err end @@ -245,18 +245,20 @@ ssl_accept(Socket, SslOptions, Timeout) when is_port(Socket) -> %% %% Description: Close an ssl connection %%-------------------------------------------------------------------- +close(#sslsocket{pid = Pid}) when is_pid(Pid) -> + ssl_connection:close(Pid); close(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}}) -> - CbMod:close(ListenSocket); -close(#sslsocket{pid = Pid}) -> - ssl_connection:close(Pid). + CbMod:close(ListenSocket). %%-------------------------------------------------------------------- -spec send(#sslsocket{}, iodata()) -> ok | {error, reason()}. %% %% Description: Sends data over the ssl connection %%-------------------------------------------------------------------- -send(#sslsocket{pid = Pid}, Data) -> - ssl_connection:send(Pid, Data). +send(#sslsocket{pid = Pid}, Data) when is_pid(Pid) -> + ssl_connection:send(Pid, Data); +send(#sslsocket{pid = {ListenSocket, #config{cb={CbModule, _, _, _}}}}, Data) -> + CbModule:send(ListenSocket, Data). %% {error,enotconn} %%-------------------------------------------------------------------- -spec recv(#sslsocket{}, integer()) -> {ok, binary()| list()} | {error, reason()}. @@ -266,8 +268,10 @@ send(#sslsocket{pid = Pid}, Data) -> %%-------------------------------------------------------------------- recv(Socket, Length) -> recv(Socket, Length, infinity). -recv(#sslsocket{pid = Pid, fd = new_ssl}, Length, Timeout) -> - ssl_connection:recv(Pid, Length, Timeout). +recv(#sslsocket{pid = Pid}, Length, Timeout) when is_pid(Pid) -> + ssl_connection:recv(Pid, Length, Timeout); +recv(#sslsocket{pid = {Listen, #config{cb={CbModule, _, _, _}}}}, _,_) when is_port(Listen)-> + CbModule:recv(Listen, 0). %% {error,enotconn} %%-------------------------------------------------------------------- -spec controlling_process(#sslsocket{}, pid()) -> ok | {error, reason()}. @@ -275,8 +279,12 @@ recv(#sslsocket{pid = Pid, fd = new_ssl}, Length, Timeout) -> %% Description: Changes process that receives the messages when active = true %% or once. %%-------------------------------------------------------------------- -controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid) -> - ssl_connection:new_user(Pid, NewOwner). +controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid), is_pid(NewOwner) -> + ssl_connection:new_user(Pid, NewOwner); +controlling_process(#sslsocket{pid = {Listen, + #config{cb={CbModule, _, _, _}}}}, NewOwner) when is_port(Listen), + is_pid(NewOwner) -> + CbModule:controlling_process(Listen, NewOwner). %%-------------------------------------------------------------------- -spec connection_info(#sslsocket{}) -> {ok, {tls_atom_version(), erl_cipher_suite()}} | @@ -284,29 +292,35 @@ controlling_process(#sslsocket{pid = Pid}, NewOwner) when is_pid(Pid) -> %% %% Description: Returns ssl protocol and cipher used for the connection %%-------------------------------------------------------------------- -connection_info(#sslsocket{pid = Pid}) -> - ssl_connection:info(Pid). +connection_info(#sslsocket{pid = Pid}) when is_pid(Pid) -> + ssl_connection:info(Pid); +connection_info(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> + {error, enotconn}. %%-------------------------------------------------------------------- -spec peername(#sslsocket{}) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. %% %% Description: same as inet:peername/1. %%-------------------------------------------------------------------- -peername(#sslsocket{pid = Pid}) -> - ssl_connection:peername(Pid). +peername(#sslsocket{pid = Pid, fd = Socket}) when is_pid(Pid)-> + inet:peername(Socket); +peername(#sslsocket{pid = {ListenSocket, _}}) -> + inet:peername(ListenSocket). %% Will return {error, enotconn} %%-------------------------------------------------------------------- -spec peercert(#sslsocket{}) ->{ok, DerCert::binary()} | {error, reason()}. %% %% Description: Returns the peercert. %%-------------------------------------------------------------------- -peercert(#sslsocket{pid = Pid}) -> +peercert(#sslsocket{pid = Pid}) when is_pid(Pid) -> case ssl_connection:peer_certificate(Pid) of {ok, undefined} -> {error, no_peercert}; Result -> Result - end. + end; +peercert(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> + {error, enotconn}. %%-------------------------------------------------------------------- -spec suite_definition(cipher_suite()) -> erl_cipher_suite(). @@ -323,7 +337,7 @@ suite_definition(S) -> %% Description: Returns the next protocol that has been negotiated. If no %% protocol has been negotiated will return {error, next_protocol_not_negotiated} %%-------------------------------------------------------------------- -negotiated_next_protocol(#sslsocket{fd = new_ssl, pid = Pid}) -> +negotiated_next_protocol(#sslsocket{pid = Pid}) -> ssl_connection:negotiated_next_protocol(Pid). -spec cipher_suites() -> [erl_cipher_suite()]. @@ -396,8 +410,9 @@ setopts(#sslsocket{}, Options) -> %% %% Description: Same as gen_tcp:shutdown/2 %%-------------------------------------------------------------------- -shutdown(#sslsocket{pid = {ListenSocket, #config{cb={CbMod,_, _, _}}}}, How) -> - CbMod:shutdown(ListenSocket, How); +shutdown(#sslsocket{pid = {Listen, #config{cb={CbMod,_, _, _}}}}, + How) when is_port(Listen) -> + CbMod:shutdown(Listen, How); shutdown(#sslsocket{pid = Pid}, How) -> ssl_connection:shutdown(Pid, How). @@ -406,11 +421,11 @@ shutdown(#sslsocket{pid = Pid}, How) -> %% %% Description: Same as inet:sockname/1 %%-------------------------------------------------------------------- -sockname(#sslsocket{pid = {ListenSocket, _}}) -> - inet:sockname(ListenSocket); +sockname(#sslsocket{pid = {Listen, _}}) when is_port(Listen) -> + inet:sockname(Listen); -sockname(#sslsocket{pid = Pid}) -> - ssl_connection:sockname(Pid). +sockname(#sslsocket{pid = Pid, fd = Socket}) when is_pid(Pid) -> + inet:sockname(Socket). %%--------------------------------------------------------------- -spec session_info(#sslsocket{}) -> {ok, list()} | {error, reason()}. @@ -418,12 +433,14 @@ sockname(#sslsocket{pid = Pid}) -> %% Description: Returns list of session info currently [{session_id, session_id(), %% {cipher_suite, cipher_suite()}] %%-------------------------------------------------------------------- -session_info(#sslsocket{pid = Pid, fd = new_ssl}) -> - ssl_connection:session_info(Pid). +session_info(#sslsocket{pid = Pid}) when is_pid(Pid) -> + ssl_connection:session_info(Pid); +session_info(#sslsocket{pid = {Listen,_}}) when is_port(Listen) -> + {error, enotconn}. %%--------------------------------------------------------------- -spec versions() -> [{ssl_app, string()} | {supported, [tls_atom_version()]} | - {available, [tls_atom_version()]}]. + {available, [tls_atom_version()]}]. %% %% Description: Returns a list of relevant versions. %%-------------------------------------------------------------------- @@ -439,8 +456,10 @@ versions() -> %% %% Description: Initiates a renegotiation. %%-------------------------------------------------------------------- -renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) -> - ssl_connection:renegotiation(Pid). +renegotiate(#sslsocket{pid = Pid}) when is_pid(Pid) -> + ssl_connection:renegotiation(Pid); +renegotiate(#sslsocket{pid = {Listen,_}}) when is_port(Listen) -> + {error, enotconn}. %%-------------------------------------------------------------------- -spec prf(#sslsocket{}, binary() | 'master_secret', binary(), @@ -449,10 +468,11 @@ renegotiate(#sslsocket{pid = Pid, fd = new_ssl}) -> %% %% 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). - +prf(#sslsocket{pid = Pid}, + Secret, Label, Seed, WantedLength) when is_pid(Pid) -> + ssl_connection:prf(Pid, Secret, Label, Seed, WantedLength); +prf(#sslsocket{pid = {Listen,_}}, _,_,_,_) when is_port(Listen) -> + {error, enotconn}. %%-------------------------------------------------------------------- -spec clear_pem_cache() -> ok. @@ -941,7 +961,5 @@ make_next_protocol_selector({server, AllProtocols, DefaultProtocol}) -> %% function in a none recommended way, but will %% work correctly if a valid pid is returned. %% Deprcated to be removed in r16 -pid(#sslsocket{fd = new_ssl}) -> - whereis(ssl_connection_sup); -pid(#sslsocket{pid = Pid}) -> - Pid. +pid(#sslsocket{})-> + whereis(ssl_connection_sup). diff --git a/lib/ssl/src/ssl_connection.erl b/lib/ssl/src/ssl_connection.erl index 23f22987df..1319b54d6b 100644 --- a/lib/ssl/src/ssl_connection.erl +++ b/lib/ssl/src/ssl_connection.erl @@ -40,8 +40,7 @@ -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, - negotiated_next_protocol/1, prf/5]). + peer_certificate/1, renegotiation/1, negotiated_next_protocol/1, prf/5]). %% Called by ssl_connection_sup -export([start_link/7]). @@ -181,7 +180,7 @@ handshake(#sslsocket{pid = Pid}, Timeout) -> socket_control(Socket, Pid, CbModule) -> case CbModule:controlling_process(Socket, Pid) of ok -> - {ok, sslsocket(Pid)}; + {ok, sslsocket(Pid, Socket)}; {error, Reason} -> {error, Reason} end. @@ -215,13 +214,7 @@ shutdown(ConnectionPid, How) -> %%-------------------------------------------------------------------- new_user(ConnectionPid, User) -> sync_send_all_state_event(ConnectionPid, {new_user, User}). -%%-------------------------------------------------------------------- --spec sockname(pid()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. -%% -%% Description: Same as inet:sockname/1 -%%-------------------------------------------------------------------- -sockname(ConnectionPid) -> - sync_send_all_state_event(ConnectionPid, sockname). + %%-------------------------------------------------------------------- -spec negotiated_next_protocol(pid()) -> {ok, binary()} | {error, reason()}. %% @@ -229,13 +222,7 @@ sockname(ConnectionPid) -> %%-------------------------------------------------------------------- negotiated_next_protocol(ConnectionPid) -> sync_send_all_state_event(ConnectionPid, negotiated_next_protocol). -%%-------------------------------------------------------------------- --spec peername(pid()) -> {ok, {inet:ip_address(), inet:port_number()}} | {error, reason()}. -%% -%% Description: Same as inet:peername/1 -%%-------------------------------------------------------------------- -peername(ConnectionPid) -> - sync_send_all_state_event(ConnectionPid, peername). + %%-------------------------------------------------------------------- -spec get_opts(pid(), list()) -> {ok, list()} | {error, reason()}. %% @@ -870,19 +857,10 @@ handle_sync_event({get_opts, OptTags}, _From, StateName, OptsReply = get_socket_opts(Socket, OptTags, SockOpts, []), {reply, OptsReply, StateName, State, get_timeout(State)}; -handle_sync_event(sockname, _From, StateName, - #state{socket = Socket} = State) -> - SockNameReply = inet:sockname(Socket), - {reply, SockNameReply, StateName, State, get_timeout(State)}; - handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = undefined} = State) -> {reply, {error, next_protocol_not_negotiated}, StateName, State, get_timeout(State)}; handle_sync_event(negotiated_next_protocol, _From, StateName, #state{next_protocol = NextProtocol} = State) -> {reply, {ok, NextProtocol}, StateName, State, get_timeout(State)}; -handle_sync_event(peername, _From, StateName, - #state{socket = Socket} = State) -> - PeerNameReply = inet:peername(Socket), - {reply, PeerNameReply, StateName, State, get_timeout(State)}; handle_sync_event({set_opts, Opts0}, _From, StateName, #state{socket_options = Opts1, @@ -1011,7 +989,7 @@ handle_info({CloseTag, Socket}, StateName, handle_info({ErrorTag, Socket, econnaborted}, StateName, #state{socket = Socket, start_or_recv_from = StartFrom, role = Role, error_tag = ErrorTag} = State) when StateName =/= connection -> - alert_user(StartFrom, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role), + alert_user(Socket, StartFrom, ?ALERT_REC(?FATAL, ?HANDSHAKE_FAILURE), Role), {stop, normal, State}; handle_info({ErrorTag, Socket, Reason}, StateName, #state{socket = Socket, @@ -1787,10 +1765,11 @@ passive_receive(State0 = #state{user_data_buffer = Buffer}, StateName) -> end. read_application_data(Data, #state{user_application = {_Mon, Pid}, - socket_options = SOpts, - bytes_to_read = BytesToRead, - start_or_recv_from = RecvFrom, - user_data_buffer = Buffer0} = State0) -> + socket = Socket, + socket_options = SOpts, + bytes_to_read = BytesToRead, + start_or_recv_from = RecvFrom, + user_data_buffer = Buffer0} = State0) -> Buffer1 = if Buffer0 =:= <<>> -> Data; Data =:= <<>> -> Buffer0; @@ -1798,7 +1777,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid}, end, case get_data(SOpts, BytesToRead, Buffer1) of {ok, ClientData, Buffer} -> % Send data - SocketOpt = deliver_app_data(SOpts, ClientData, Pid, RecvFrom), + SocketOpt = deliver_app_data(Socket, SOpts, ClientData, Pid, RecvFrom), State = State0#state{user_data_buffer = Buffer, start_or_recv_from = undefined, bytes_to_read = 0, @@ -1815,7 +1794,7 @@ read_application_data(Data, #state{user_application = {_Mon, Pid}, {more, Buffer} -> % no reply, we need more data next_record(State0#state{user_data_buffer = Buffer}); {error,_Reason} -> %% Invalid packet in packet mode - deliver_packet_error(SOpts, Buffer1, Pid, RecvFrom), + deliver_packet_error(Socket, SOpts, Buffer1, Pid, RecvFrom), {stop, normal, State0} end. @@ -1894,9 +1873,9 @@ decode_packet(Type, Buffer, PacketOpts) -> %% Note that if the user has explicitly configured the socket to expect %% HTTP headers using the {packet, httph} option, we don't do any automatic %% switching of states. -deliver_app_data(SOpts = #socket_options{active=Active, packet=Type}, - Data, Pid, From) -> - send_or_reply(Active, Pid, From, format_reply(SOpts, Data)), +deliver_app_data(Socket, SOpts = #socket_options{active=Active, packet=Type}, + Data, Pid, From) -> + send_or_reply(Active, Pid, From, format_reply(Socket, SOpts, Data)), SO = case Data of {P, _, _, _} when ((P =:= http_request) or (P =:= http_response)), ((Type =:= http) or (Type =:= http_bin)) -> @@ -1915,31 +1894,31 @@ deliver_app_data(SOpts = #socket_options{active=Active, packet=Type}, SO end. -format_reply(#socket_options{active = false, mode = Mode, packet = Packet, +format_reply(_,#socket_options{active = false, mode = Mode, packet = Packet, header = Header}, Data) -> - {ok, format_reply(Mode, Packet, Header, Data)}; -format_reply(#socket_options{active = _, mode = Mode, packet = Packet, + {ok, do_format_reply(Mode, Packet, Header, Data)}; +format_reply(Socket, #socket_options{active = _, mode = Mode, packet = Packet, header = Header}, Data) -> - {ssl, sslsocket(), format_reply(Mode, Packet, Header, Data)}. + {ssl, sslsocket(self(), Socket), do_format_reply(Mode, Packet, Header, Data)}. -deliver_packet_error(SO= #socket_options{active = Active}, Data, Pid, From) -> - send_or_reply(Active, Pid, From, format_packet_error(SO, Data)). +deliver_packet_error(Socket, SO= #socket_options{active = Active}, Data, Pid, From) -> + send_or_reply(Active, Pid, From, format_packet_error(Socket, SO, Data)). -format_packet_error(#socket_options{active = false, mode = Mode}, Data) -> - {error, {invalid_packet, format_reply(Mode, raw, 0, Data)}}; -format_packet_error(#socket_options{active = _, mode = Mode}, Data) -> - {ssl_error, sslsocket(), {invalid_packet, format_reply(Mode, raw, 0, Data)}}. +format_packet_error(_,#socket_options{active = false, mode = Mode}, Data) -> + {error, {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}; +format_packet_error(Socket, #socket_options{active = _, mode = Mode}, Data) -> + {ssl_error, sslsocket(self(), Socket), {invalid_packet, do_format_reply(Mode, raw, 0, Data)}}. -format_reply(binary, _, N, Data) when N > 0 -> % Header mode +do_format_reply(binary, _, N, Data) when N > 0 -> % Header mode header(N, Data); -format_reply(binary, _, _, Data) -> +do_format_reply(binary, _, _, Data) -> Data; -format_reply(list, Packet, _, Data) +do_format_reply(list, Packet, _, Data) when Packet == http; Packet == {http, headers}; Packet == http_bin; Packet == {http_bin, headers}; Packet == httph; Packet == httph_bin -> Data; -format_reply(list, _,_, Data) -> +do_format_reply(list, _,_, Data) -> binary_to_list(Data). header(0, <<>>) -> @@ -2171,11 +2150,8 @@ initial_state(Role, Host, Port, Socket, {SSLOptions, SocketOptions}, User, send_queue = queue:new() }. -sslsocket(Pid) -> - #sslsocket{pid = Pid, fd = new_ssl}. - -sslsocket() -> - sslsocket(self()). +sslsocket(Pid, Socket) -> + #sslsocket{pid = Pid, fd = Socket}. get_socket_opts(_,[], _, Acc) -> {ok, Acc}; @@ -2271,12 +2247,12 @@ handle_alerts([Alert | Alerts], {next_state, StateName, State, _Timeout}) -> handle_alerts(Alerts, handle_alert(Alert, StateName, State)). handle_alert(#alert{level = ?FATAL} = Alert, StateName, - #state{start_or_recv_from = From, host = Host, port = Port, session = Session, - user_application = {_Mon, Pid}, + #state{socket = Socket, start_or_recv_from = From, host = Host, + port = Port, session = Session, user_application = {_Mon, Pid}, log_alert = Log, role = Role, socket_options = Opts} = State) -> invalidate_session(Role, Host, Port, Session), log_alert(Log, StateName, Alert), - alert_user(StateName, Opts, Pid, From, Alert, Role), + alert_user(Socket, StateName, Opts, Pid, From, Alert, Role), {stop, normal, State}; handle_alert(#alert{level = ?WARNING, description = ?CLOSE_NOTIFY} = Alert, @@ -2303,28 +2279,28 @@ handle_alert(#alert{level = ?WARNING, description = ?USER_CANCELED} = Alert, Sta {Record, State} = next_record(State0), next_state(StateName, StateName, Record, State). -alert_user(connection, Opts, Pid, From, Alert, Role) -> - alert_user(Opts#socket_options.active, Pid, From, Alert, Role); -alert_user(_, _, _, From, Alert, Role) -> - alert_user(From, Alert, Role). +alert_user(Socket, connection, Opts, Pid, From, Alert, Role) -> + alert_user(Socket, Opts#socket_options.active, Pid, From, Alert, Role); +alert_user(Socket,_, _, _, From, Alert, Role) -> + alert_user(Socket, From, Alert, Role). -alert_user(From, Alert, Role) -> - alert_user(false, no_pid, From, Alert, Role). +alert_user(Socket, From, Alert, Role) -> + alert_user(Socket, false, no_pid, From, Alert, Role). -alert_user(false = Active, Pid, From, Alert, Role) -> +alert_user(_Socket, false = Active, Pid, From, Alert, Role) -> %% If there is an outstanding ssl_accept | recv %% From will be defined and send_or_reply will %% send the appropriate error message. ReasonCode = ssl_alert:reason_code(Alert, Role), send_or_reply(Active, Pid, From, {error, ReasonCode}); -alert_user(Active, Pid, From, Alert, Role) -> +alert_user(Socket, Active, Pid, From, Alert, Role) -> case ssl_alert:reason_code(Alert, Role) of closed -> send_or_reply(Active, Pid, From, - {ssl_closed, sslsocket()}); + {ssl_closed, sslsocket(self(), Socket)}); ReasonCode -> send_or_reply(Active, Pid, From, - {ssl_error, sslsocket(), ReasonCode}) + {ssl_error, sslsocket(self(), Socket), ReasonCode}) end. log_alert(true, Info, Alert) -> @@ -2353,13 +2329,16 @@ handle_own_alert(Alert, Version, StateName, ok end. -handle_normal_shutdown(Alert, _, #state{start_or_recv_from = StartFrom, role = Role, renegotiation = {false, first}}) -> - alert_user(StartFrom, Alert, Role); +handle_normal_shutdown(Alert, _, #state{socket = Socket, + start_or_recv_from = StartFrom, + role = Role, renegotiation = {false, first}}) -> + alert_user(Socket, StartFrom, Alert, Role); -handle_normal_shutdown(Alert, StateName, #state{socket_options = Opts, +handle_normal_shutdown(Alert, StateName, #state{socket = Socket, + socket_options = Opts, user_application = {_Mon, Pid}, start_or_recv_from = RecvFrom, role = Role}) -> - alert_user(StateName, Opts, Pid, RecvFrom, Alert, Role). + alert_user(Socket, StateName, Opts, Pid, RecvFrom, Alert, Role). handle_unexpected_message(Msg, Info, #state{negotiated_version = Version} = State) -> Alert = ?ALERT_REC(?FATAL,?UNEXPECTED_MESSAGE), diff --git a/lib/ssl/test/ssl_basic_SUITE.erl b/lib/ssl/test/ssl_basic_SUITE.erl index 6cf712fa6f..a202aca943 100644 --- a/lib/ssl/test/ssl_basic_SUITE.erl +++ b/lib/ssl/test/ssl_basic_SUITE.erl @@ -257,7 +257,8 @@ api_tests() -> shutdown_write, shutdown_both, shutdown_error, - hibernate + hibernate, + listen_socket ]. certificate_verify_tests() -> @@ -3777,6 +3778,35 @@ hibernate(Config) -> ssl_test_lib:close(Client). %%-------------------------------------------------------------------- +listen_socket(doc) -> + ["Check error handling and inet compliance when calling API functions with listen sockets."]; + +listen_socket(suite) -> + []; + +listen_socket(Config) -> + ServerOpts = ?config(server_opts, Config), + {ok, ListenSocket} = ssl:listen(0, ServerOpts), + + %% This can be a valid thing to do as + %% options are inherited by the accept socket + ok = ssl:controlling_process(ListenSocket, self()), + + {ok, _} = ssl:sockname(ListenSocket), + + {error, enotconn} = ssl:send(ListenSocket, <<"data">>), + {error, enotconn} = ssl:recv(ListenSocket, 0), + {error, enotconn} = ssl:connection_info(ListenSocket), + {error, enotconn} = ssl:peername(ListenSocket), + {error, enotconn} = ssl:peercert(ListenSocket), + {error, enotconn} = ssl:session_info(ListenSocket), + {error, enotconn} = ssl:renegotiate(ListenSocket), + {error, enotconn} = ssl:prf(ListenSocket, 'master_secret', <<"Label">>, client_random, 256), + {error, enotconn} = ssl:shutdown(ListenSocket, read_write), + + ok = ssl:close(ListenSocket). + +%%-------------------------------------------------------------------- connect_twice(doc) -> [""]; diff --git a/lib/ssl/test/ssl_to_openssl_SUITE.erl b/lib/ssl/test/ssl_to_openssl_SUITE.erl index 21797bee08..98ef050b14 100644 --- a/lib/ssl/test/ssl_to_openssl_SUITE.erl +++ b/lib/ssl/test/ssl_to_openssl_SUITE.erl @@ -1451,11 +1451,27 @@ check_sane_openssl_renegotaite(Config) -> end. check_sane_openssl_sslv2(Config) -> - case os:cmd("openssl version") of - "OpenSSL 1." ++ _ -> - {skip, "sslv2 by default turned of in 1.*"}; - _ -> - Config + Port = open_port({spawn, "openssl s_client -ssl2 "}, [stderr_to_stdout]), + case supports_sslv2(Port) of + true -> + Config; + false -> + {skip, "sslv2 not supported by openssl"} + end. + +supports_sslv2(Port) -> + receive + {Port, {data, "unknown option -ssl2" ++ _}} -> + false; + {Port, {data, Data}} -> + case lists:member("error", string:tokens(Data, ":")) of + true -> + false; + false -> + supports_sslv2(Port) + end + after 500 -> + true end. check_sane_openssl_version(Version) -> diff --git a/lib/stdlib/src/binary.erl b/lib/stdlib/src/binary.erl index 1ed3422bc5..41b6ab1d5f 100644 --- a/lib/stdlib/src/binary.erl +++ b/lib/stdlib/src/binary.erl @@ -21,6 +21,8 @@ %% Implemented in this module: -export([split/2,split/3,replace/3,replace/4]). +-export_type([cp/0]). + -opaque cp() :: {'am' | 'bm', binary()}. -type part() :: {Start :: non_neg_integer(), Length :: integer()}. diff --git a/lib/stdlib/src/dets.erl b/lib/stdlib/src/dets.erl index 6a937f8fa2..845fae4bf4 100644 --- a/lib/stdlib/src/dets.erl +++ b/lib/stdlib/src/dets.erl @@ -88,7 +88,8 @@ %% Not documented, or not ready for publication. -export([lookup_keys/2]). --export_type([tab_name/0]). +-export_type([bindings_cont/0, cont/0, object_cont/0, select_cont/0, + tab_name/0]). -compile({inline, [{einval,2},{badarg,2},{undefined,1}, {badarg_exit,2},{lookup_reply,2}]}). diff --git a/lib/stdlib/src/erl_scan.erl b/lib/stdlib/src/erl_scan.erl index 8e59e01f48..0c8735bb6d 100644 --- a/lib/stdlib/src/erl_scan.erl +++ b/lib/stdlib/src/erl_scan.erl @@ -55,7 +55,7 @@ token_info/1,token_info/2, attributes_info/1,attributes_info/2,set_attribute/3]). --export_type([error_info/0, line/0, tokens_result/0]). +-export_type([error_info/0, line/0, return_cont/0, tokens_result/0]). %%% %%% Defines and type definitions diff --git a/lib/stdlib/src/ets.erl b/lib/stdlib/src/ets.erl index ee6cff1b75..61bb038737 100644 --- a/lib/stdlib/src/ets.erl +++ b/lib/stdlib/src/ets.erl @@ -42,7 +42,7 @@ -export([i/0, i/1, i/2, i/3]). --export_type([tab/0, tid/0, match_spec/0, comp_match_spec/0]). +-export_type([tab/0, tid/0, match_spec/0, comp_match_spec/0, match_pattern/0]). %%----------------------------------------------------------------------------- diff --git a/lib/stdlib/src/gb_sets.erl b/lib/stdlib/src/gb_sets.erl index 91d21d869c..391f1cff64 100644 --- a/lib/stdlib/src/gb_sets.erl +++ b/lib/stdlib/src/gb_sets.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 @@ -196,6 +196,8 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Some types. +-export_type([iter/0]). + -type gb_set_node() :: 'nil' | {term(), _, _}. -opaque iter() :: [gb_set_node()]. diff --git a/lib/stdlib/src/gb_trees.erl b/lib/stdlib/src/gb_trees.erl index 6ad861ff5b..258713c90f 100644 --- a/lib/stdlib/src/gb_trees.erl +++ b/lib/stdlib/src/gb_trees.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2001-2011. All Rights Reserved. +%% Copyright Ericsson AB 2001-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 @@ -152,6 +152,8 @@ %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %% Some types. +-export_type([iter/0]). + -type gb_tree_node() :: 'nil' | {_, _, _, _}. -opaque iter() :: [gb_tree_node()]. diff --git a/lib/stdlib/src/log_mf_h.erl b/lib/stdlib/src/log_mf_h.erl index f7f128dac7..19b555a48c 100644 --- a/lib/stdlib/src/log_mf_h.erl +++ b/lib/stdlib/src/log_mf_h.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-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 @@ -25,6 +25,8 @@ -export([init/1, handle_event/2, handle_info/2, terminate/2]). -export([handle_call/2, code_change/3]). +-export_type([args/0]). + %%----------------------------------------------------------------- -type b() :: non_neg_integer(). diff --git a/lib/stdlib/src/qlc.erl b/lib/stdlib/src/qlc.erl index 2b691e6abf..9b71d0edb8 100644 --- a/lib/stdlib/src/qlc.erl +++ b/lib/stdlib/src/qlc.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 2004-2011. All Rights Reserved. +%% Copyright Ericsson AB 2004-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 @@ -125,7 +125,7 @@ -define(THROWN_ERROR, {?MODULE, throw_error, _, _}). --export_type([query_handle/0]). +-export_type([query_cursor/0, query_handle/0]). %%% A query handle is a tuple {qlc_handle, Handle} where Handle is one %%% of #qlc_append, #qlc_table, #qlc_sort, and #qlc_lc. diff --git a/lib/stdlib/src/sys.erl b/lib/stdlib/src/sys.erl index f34201604c..4dd70ad425 100644 --- a/lib/stdlib/src/sys.erl +++ b/lib/stdlib/src/sys.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1996-2011. All Rights Reserved. +%% Copyright Ericsson AB 1996-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 @@ -32,6 +32,8 @@ %% Types %%----------------------------------------------------------------- +-export_type([dbg_opt/0]). + -type name() :: pid() | atom() | {'global', atom()}. -type system_event() :: {'in', Msg :: _} | {'in', Msg :: _, From :: _} diff --git a/lib/stdlib/src/win32reg.erl b/lib/stdlib/src/win32reg.erl index 598e77ffdc..48a7e262be 100644 --- a/lib/stdlib/src/win32reg.erl +++ b/lib/stdlib/src/win32reg.erl @@ -1,7 +1,7 @@ %% %% %CopyrightBegin% %% -%% Copyright Ericsson AB 1997-2011. All Rights Reserved. +%% Copyright Ericsson AB 1997-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 @@ -25,6 +25,8 @@ expand/1, format_error/1]). +-export_type([reg_handle/0]). + %% Key handles (always open). -define(hkey_classes_root, 16#80000000). -define(hkey_current_user, 16#80000001). diff --git a/lib/test_server/src/ts_run.erl b/lib/test_server/src/ts_run.erl index f4d5b3e3b1..57d1b8806e 100644 --- a/lib/test_server/src/ts_run.erl +++ b/lib/test_server/src/ts_run.erl @@ -380,13 +380,7 @@ make_common_test_args(Args0, Options0, _Vars) -> [{logdir,"../test_server"}] end, - TimeTrap = case test_server:timetrap_scale_factor() of - 1 -> - []; - Scale -> - [{multiply_timetraps, Scale}, - {scale_timetraps, true}] - end, + TimeTrap = [{scale_timetraps, true}], {ConfigPath, Options} = case {os:getenv("TEST_CONFIG_PATH"), |