1%%
2%% %CopyrightBegin%
3%%
4%% Copyright Ericsson AB 2008-2020. All Rights Reserved.
5%%
6%% Licensed under the Apache License, Version 2.0 (the "License");
7%% you may not use this file except in compliance with the License.
8%% You may obtain a copy of the License at
9%%
10%%     http://www.apache.org/licenses/LICENSE-2.0
11%%
12%% Unless required by applicable law or agreed to in writing, software
13%% distributed under the License is distributed on an "AS IS" BASIS,
14%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15%% See the License for the specific language governing permissions and
16%% limitations under the License.
17%%
18%% %CopyrightEnd%
19%%
20%%
21%%----------------------------------------------------------------------
22%% Purpose: Handles an ssh connection, e.i. both the
23%% setup SSH Transport Layer Protocol (RFC 4253), Authentication
24%% Protocol (RFC 4252) and SSH connection Protocol (RFC 4255)
25%% Details of the different protocols are
26%% implemented in ssh_transport.erl, ssh_auth.erl and ssh_connection.erl
27%% ----------------------------------------------------------------------
28
29-module(ssh_connection_handler).
30
31-behaviour(gen_statem).
32
33-include("ssh.hrl").
34-include("ssh_transport.hrl").
35-include("ssh_auth.hrl").
36-include("ssh_connect.hrl").
37
38%%====================================================================
39%%% Exports
40%%====================================================================
41
42%%% Start and stop
43-export([start_link/3,
44	 stop/1
45	]).
46
47%%% Internal application API
48-export([start_connection/4,
49         available_hkey_algorithms/2,
50	 open_channel/6,
51         start_channel/5,
52         handle_direct_tcpip/6,
53	 request/6, request/7,
54	 reply_request/3,
55         global_request/5,
56	 send/5,
57	 send_eof/2,
58         store/3,
59         retrieve/2,
60	 info/1, info/2,
61	 connection_info/2,
62	 channel_info/3,
63	 adjust_window/3, close/2,
64	 disconnect/4,
65	 get_print_info/1,
66         set_sock_opts/2, get_sock_opts/2,
67         prohibited_sock_option/1
68	]).
69
70-type connection_ref() :: ssh:connection_ref().
71-type channel_id()     :: ssh:channel_id().
72
73%%% Behaviour callbacks
74-export([init/1, callback_mode/0, handle_event/4, terminate/3,
75	 format_status/2, code_change/4]).
76
77%%% Exports not intended to be used :). They are used for spawning and tests
78-export([init_connection_handler/3,	   % proc_lib:spawn needs this
79	 init_ssh_record/3,		   % Export of this internal function
80					   % intended for low-level protocol test suites
81	 renegotiate/1, alg/1 % Export intended for test cases
82	]).
83
84-behaviour(ssh_dbg).
85-export([ssh_dbg_trace_points/0, ssh_dbg_flags/1, ssh_dbg_on/1, ssh_dbg_off/1, ssh_dbg_format/2]).
86
87
88-define(send_disconnect(Code, DetailedText, StateName, State),
89        send_disconnect(Code, DetailedText, ?MODULE, ?LINE, StateName, State)).
90
91-define(send_disconnect(Code, Reason, DetailedText, StateName, State),
92        send_disconnect(Code, Reason, DetailedText, ?MODULE, ?LINE, StateName, State)).
93
94-define(call_disconnectfun_and_log_cond(LogMsg, DetailedText, StateName, D),
95        call_disconnectfun_and_log_cond(LogMsg, DetailedText, ?MODULE, ?LINE, StateName, D)).
96
97%%====================================================================
98%% Start / stop
99%%====================================================================
100%%--------------------------------------------------------------------
101-spec start_link(role(),
102		 gen_tcp:socket(),
103                 internal_options()
104		) -> {ok, pid()}.
105%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
106start_link(Role, Socket, Options) ->
107    {ok, proc_lib:spawn_opt(?MODULE,
108                            init_connection_handler,
109                            [Role, Socket, Options],
110                            [link, {message_queue_data,off_heap}]
111                           )}.
112
113
114%%--------------------------------------------------------------------
115-spec stop(connection_ref()
116	  ) -> ok | {error, term()}.
117%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
118stop(ConnectionHandler)->
119    case call(ConnectionHandler, stop) of
120       {error, closed} ->
121	    ok;
122	Other ->
123	    Other
124    end.
125
126%%====================================================================
127%% Internal application API
128%%====================================================================
129
130%%--------------------------------------------------------------------
131-spec start_connection(role(),
132		       gen_tcp:socket(),
133                       internal_options(),
134		       timeout()
135		      ) -> {ok, connection_ref()} | {error, term()}.
136%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
137start_connection(Role, Socket, Options, Timeout) ->
138    try
139        case Role of
140            client ->
141                ChildPid = start_the_connection_child(self(), Role, Socket, Options),
142                handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout);
143            server ->
144                case ?GET_OPT(parallel_login, Options) of
145                    true ->
146                        HandshakerPid =
147                            spawn_link(fun() ->
148                                               receive
149                                                   {do_handshake, Pid} ->
150                                                       handshake(Pid, erlang:monitor(process,Pid), Timeout)
151                                               end
152                                       end),
153                        ChildPid = start_the_connection_child(HandshakerPid, Role, Socket, Options),
154                        HandshakerPid ! {do_handshake, ChildPid};
155                    false ->
156                        ChildPid = start_the_connection_child(self(), Role, Socket, Options),
157                        handshake(ChildPid, erlang:monitor(process,ChildPid), Timeout)
158                end
159        end
160    catch
161	exit:{noproc, _} ->
162	    {error, ssh_not_started};
163	_:Error ->
164	    {error, Error}
165    end.
166
167%%--------------------------------------------------------------------
168%%% Some other module has decided to disconnect.
169
170-spec disconnect(Code::integer(), Details::iodata(),
171                      Module::atom(), Line::integer()) -> no_return().
172%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
173
174% Preferable called with the macro ?DISCONNECT
175
176disconnect(Code, DetailedText, Module, Line) ->
177    throw({keep_state_and_data,
178	   [{next_event, internal, {send_disconnect, Code, DetailedText, Module, Line}}]}).
179
180%%--------------------------------------------------------------------
181%%% Open a channel in the connection to the peer, that is, do the ssh
182%%% signalling with the peer.
183-spec open_channel(connection_ref(),
184		   string(),
185		   iodata(),
186		   pos_integer(),
187		   pos_integer(),
188		   timeout()
189		  ) -> {open, channel_id()} | {error, term()}.
190
191%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
192open_channel(ConnectionHandler,
193	     ChannelType, ChannelSpecificData, InitialWindowSize, MaxPacketSize,
194	     Timeout) ->
195    call(ConnectionHandler,
196	 {open,
197	  self(),
198	  ChannelType, InitialWindowSize, MaxPacketSize, ChannelSpecificData,
199	  Timeout}).
200
201%%--------------------------------------------------------------------
202%%% Start a channel handling process in the superviser tree
203-spec start_channel(connection_ref(), atom(), channel_id(), list(), term()) ->
204                           {ok, pid()} | {error, term()}.
205
206%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
207start_channel(ConnectionHandler, CallbackModule, ChannelId, Args, Exec) ->
208    {ok, {SubSysSup,Role,Opts}} = call(ConnectionHandler, get_misc),
209    ssh_subsystem_sup:start_channel(Role, SubSysSup,
210                                    ConnectionHandler, CallbackModule, ChannelId,
211                                    Args, Exec, Opts).
212
213%%--------------------------------------------------------------------
214handle_direct_tcpip(ConnectionHandler, ListenHost, ListenPort, ConnectToHost, ConnectToPort, Timeout) ->
215    call(ConnectionHandler, {handle_direct_tcpip, ListenHost, ListenPort, ConnectToHost, ConnectToPort, Timeout}).
216
217%%--------------------------------------------------------------------
218-spec request(connection_ref(),
219	      pid(),
220	      channel_id(),
221	      string(),
222	      boolean(),
223	      iodata(),
224	      timeout()
225	     ) -> success | failure | ok | {error,timeout}.
226
227-spec request(connection_ref(),
228	      channel_id(),
229	      string(),
230	      boolean(),
231	      iodata(),
232	      timeout()
233	     ) -> success | failure | ok | {error,timeout}.
234%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
235request(ConnectionHandler, ChannelPid, ChannelId, Type, true, Data, Timeout) ->
236    call(ConnectionHandler, {request, ChannelPid, ChannelId, Type, Data, Timeout});
237request(ConnectionHandler, ChannelPid, ChannelId, Type, false, Data, _) ->
238    cast(ConnectionHandler, {request, ChannelPid, ChannelId, Type, Data}).
239
240request(ConnectionHandler, ChannelId, Type, true, Data, Timeout) ->
241    call(ConnectionHandler, {request, ChannelId, Type, Data, Timeout});
242request(ConnectionHandler, ChannelId, Type, false, Data, _) ->
243    cast(ConnectionHandler, {request, ChannelId, Type, Data}).
244
245%%--------------------------------------------------------------------
246-spec reply_request(connection_ref(),
247		    success | failure,
248		    channel_id()
249		   ) -> ok.
250
251%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
252reply_request(ConnectionHandler, Status, ChannelId) ->
253    cast(ConnectionHandler, {reply_request, Status, ChannelId}).
254
255%%--------------------------------------------------------------------
256global_request(ConnectionHandler, Type, true, Data, Timeout) ->
257    call(ConnectionHandler, {global_request, Type, Data, Timeout});
258global_request(ConnectionHandler, Type, false, Data, _) ->
259    cast(ConnectionHandler, {global_request, Type, Data}).
260
261%%--------------------------------------------------------------------
262-spec send(connection_ref(),
263	   channel_id(),
264	   non_neg_integer(),
265	   iodata(),
266	   timeout()
267	  ) -> ok | {error, timeout|closed}.
268%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
269send(ConnectionHandler, ChannelId, Type, Data, Timeout) ->
270    call(ConnectionHandler, {data, ChannelId, Type, Data, Timeout}).
271
272%%--------------------------------------------------------------------
273-spec send_eof(connection_ref(),
274	       channel_id()
275	      ) -> ok | {error,closed}.
276%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
277send_eof(ConnectionHandler, ChannelId) ->
278    call(ConnectionHandler, {eof, ChannelId}).
279
280%%--------------------------------------------------------------------
281-spec info(connection_ref()
282	  ) -> {ok, [#channel{}]} .
283
284-spec info(connection_ref(),
285	   pid() | all
286	  ) -> {ok, [#channel{}]} .
287%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
288info(ConnectionHandler) ->
289    info(ConnectionHandler, all).
290
291info(ConnectionHandler, ChannelProcess) ->
292    call(ConnectionHandler, {info, ChannelProcess}).
293
294%%--------------------------------------------------------------------
295-type local_sock_info() :: {inet:ip_address(), non_neg_integer()} | string().
296-type peer_sock_info()  :: {inet:ip_address(), non_neg_integer()} | string().
297-type state_info() :: iolist().
298
299-spec get_print_info(connection_ref()
300		    ) -> {{local_sock_info(), peer_sock_info()},
301			  state_info()
302			 }.
303%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
304get_print_info(ConnectionHandler) ->
305    call(ConnectionHandler, get_print_info, 1000).
306
307%%--------------------------------------------------------------------
308connection_info(ConnectionHandler, []) ->
309    connection_info(ConnectionHandler, conn_info_keys());
310connection_info(ConnectionHandler, Key) when is_atom(Key) ->
311    case connection_info(ConnectionHandler, [Key]) of
312        [{Key,Val}] -> {Key,Val};
313        Other -> Other
314    end;
315connection_info(ConnectionHandler, Options) ->
316    call(ConnectionHandler, {connection_info, Options}).
317
318%%--------------------------------------------------------------------
319-spec channel_info(connection_ref(),
320		   channel_id(),
321		   [atom()]
322		  ) -> proplists:proplist().
323%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
324channel_info(ConnectionHandler, ChannelId, Options) ->
325    call(ConnectionHandler, {channel_info, ChannelId, Options}).
326
327%%--------------------------------------------------------------------
328-spec adjust_window(connection_ref(),
329		    channel_id(),
330		    integer()
331		   ) -> ok.
332%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
333adjust_window(ConnectionHandler, Channel, Bytes) ->
334    cast(ConnectionHandler, {adjust_window, Channel, Bytes}).
335
336%%--------------------------------------------------------------------
337-spec close(connection_ref(),
338	    channel_id()
339	   ) -> ok.
340%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
341close(ConnectionHandler, ChannelId) ->
342    case call(ConnectionHandler, {close, ChannelId}) of
343	ok ->
344	    ok;
345	{error, closed} ->
346	    ok
347    end.
348
349
350%%--------------------------------------------------------------------
351store(ConnectionHandler, Key, Value) ->
352    cast(ConnectionHandler, {store,Key,Value}).
353
354retrieve(#connection{options=Opts}, Key) ->
355    try ?GET_INTERNAL_OPT(Key, Opts) of
356        Value ->
357            {ok,Value}
358    catch
359        error:{badkey,Key} ->
360            undefined
361    end;
362retrieve(ConnectionHandler, Key) ->
363    call(ConnectionHandler, {retrieve,Key}).
364
365%%--------------------------------------------------------------------
366%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
367set_sock_opts(ConnectionRef, SocketOptions) ->
368    try lists:foldr(fun({Name,_Val}, Acc) ->
369                            case prohibited_sock_option(Name) of
370                                true -> [Name|Acc];
371                                false -> Acc
372                            end
373                    end, [], SocketOptions)
374    of
375        [] ->
376            call(ConnectionRef, {set_sock_opts,SocketOptions});
377        Bad ->
378            {error, {not_allowed,Bad}}
379    catch
380        _:_ ->
381            {error, badarg}
382    end.
383
384prohibited_sock_option(active)    -> true;
385prohibited_sock_option(deliver)   -> true;
386prohibited_sock_option(mode)      -> true;
387prohibited_sock_option(packet)    -> true;
388prohibited_sock_option(_) -> false.
389
390%%--------------------------------------------------------------------
391%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
392get_sock_opts(ConnectionRef, SocketGetOptions) ->
393    call(ConnectionRef, {get_sock_opts,SocketGetOptions}).
394
395%%====================================================================
396%% Test support
397%%====================================================================
398%%--------------------------------------------------------------------
399-spec renegotiate(connection_ref()
400		 ) -> ok.
401%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
402renegotiate(ConnectionHandler) ->
403    cast(ConnectionHandler, force_renegotiate).
404
405%%--------------------------------------------------------------------
406alg(ConnectionHandler) ->
407    call(ConnectionHandler, get_alg).
408
409%%====================================================================
410%% Internal process state
411%%====================================================================
412-record(data, {
413	  starter                               :: pid()
414						 | undefined,
415	  auth_user                             :: string()
416						 | undefined,
417	  connection_state                      :: #connection{}
418						 | undefined,
419	  latest_channel_id         = 0         :: non_neg_integer()
420                                                 | undefined,
421	  transport_protocol                    :: atom()
422                                                 | undefined,	% ex: tcp
423	  transport_cb                          :: atom()
424                                                 | undefined,	% ex: gen_tcp
425	  transport_close_tag                   :: atom()
426                                                 | undefined,	% ex: tcp_closed
427	  ssh_params                            :: #ssh{}
428                                                 | undefined,
429	  socket                                :: gen_tcp:socket()
430                                                 | undefined,
431	  decrypted_data_buffer     = <<>>      :: binary()
432                                                 | undefined,
433	  encrypted_data_buffer     = <<>>      :: binary()
434                                                 | undefined,
435	  aead_data                 = <<>>      :: binary()
436                                                 | undefined,
437	  undecrypted_packet_length             :: undefined | non_neg_integer(),
438	  key_exchange_init_msg                 :: #ssh_msg_kexinit{}
439						 | undefined,
440	  last_size_rekey           = 0         :: non_neg_integer(),
441	  event_queue               = []        :: list(),
442	  inet_initial_recbuf_size              :: pos_integer()
443						 | undefined
444	 }).
445
446%%====================================================================
447%% Intitialisation
448%%====================================================================
449%%--------------------------------------------------------------------
450-spec init_connection_handler(role(),
451			      gen_tcp:socket(),
452			      internal_options()
453			     ) -> no_return().
454%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
455init_connection_handler(Role, Socket, Opts) ->
456    case init([Role, Socket, Opts]) of
457        {ok, StartState, D} when Role == server ->
458            process_flag(trap_exit, true),
459            gen_statem:enter_loop(?MODULE,
460                                  [], %%[{debug,[trace,log,statistics,debug]} ], %% []
461                                  StartState,
462                                  D);
463
464        {ok, StartState, D0=#data{connection_state=C}} when Role == client ->
465            process_flag(trap_exit, true),
466            Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
467            D = D0#data{connection_state =
468                            C#connection{system_supervisor =     proplists:get_value(system_sup,     Sups),
469                                         sub_system_supervisor = proplists:get_value(subsystem_sup,  Sups),
470                                         connection_supervisor = proplists:get_value(connection_sup, Sups)
471                                        }},
472            gen_statem:enter_loop(?MODULE,
473                                  [], %%[{debug,[trace,log,statistics,debug]} ], %% []
474                                  StartState,
475                                  D);
476
477        {stop, Error} ->
478            D = try
479                    %% Only servers have supervisorts defined in Opts
480                    Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
481                    #connection{system_supervisor =     proplists:get_value(system_sup,     Sups),
482                                sub_system_supervisor = proplists:get_value(subsystem_sup,  Sups),
483                                connection_supervisor = proplists:get_value(connection_sup, Sups)
484                               }
485                of
486                    C ->
487                        #data{connection_state=C}
488                catch
489                    _:_ ->
490                        #data{connection_state=#connection{}}
491                end,
492            gen_statem:enter_loop(?MODULE,
493                                  [],
494                                  {init_error,Error},
495                                  D#data{socket=Socket})
496    end.
497
498
499
500init([Role,Socket,Opts]) ->
501    case inet:peername(Socket) of
502        {ok, PeerAddr} ->
503            {Protocol, Callback, CloseTag} = ?GET_OPT(transport, Opts),
504            C = #connection{channel_cache = ssh_client_channel:cache_create(),
505                            channel_id_seed = 0,
506                            requests = [],
507                            options = Opts},
508            D0 = #data{starter = ?GET_INTERNAL_OPT(user_pid, Opts),
509                       connection_state = C,
510                       socket = Socket,
511                       transport_protocol = Protocol,
512                       transport_cb = Callback,
513                       transport_close_tag = CloseTag,
514                       ssh_params = init_ssh_record(Role, Socket, PeerAddr, Opts)
515              },
516            D = case Role of
517                    client ->
518                        D0;
519                    server ->
520                        Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
521                        D0#data{connection_state =
522                                    C#connection{cli_spec = ?GET_OPT(ssh_cli, Opts, {ssh_cli,[?GET_OPT(shell, Opts)]}),
523                                                 exec =     ?GET_OPT(exec,    Opts),
524                                                 system_supervisor =     proplists:get_value(system_sup,     Sups),
525                                                 sub_system_supervisor = proplists:get_value(subsystem_sup,  Sups),
526                                                 connection_supervisor = proplists:get_value(connection_sup, Sups)
527                                                }}
528                end,
529            {ok, {hello,Role}, D};
530
531        {error,Error} ->
532            {stop, Error}
533    end.
534
535
536
537init_ssh_record(Role, Socket, Opts) ->
538    %% Export of this internal function is
539    %% intended for low-level protocol test suites
540    {ok,PeerAddr} = inet:peername(Socket),
541    init_ssh_record(Role, Socket, PeerAddr, Opts).
542
543init_ssh_record(Role, Socket, PeerAddr, Opts) ->
544    AuthMethods = ?GET_OPT(auth_methods, Opts),
545    S0 = #ssh{role = Role,
546	      opts = Opts,
547	      userauth_supported_methods = AuthMethods,
548	      available_host_keys = available_hkey_algorithms(Role, Opts),
549	      random_length_padding = ?GET_OPT(max_random_length_padding, Opts)
550	   },
551
552    {Vsn, Version} = ssh_transport:versions(Role, Opts),
553    LocalName = case inet:sockname(Socket) of
554                    {ok,Local} -> Local;
555                    _ -> undefined
556                end,
557    case Role of
558	client ->
559	    PeerName = case ?GET_INTERNAL_OPT(host, Opts, element(1,PeerAddr)) of
560                           PeerIP when is_tuple(PeerIP) ->
561                               inet_parse:ntoa(PeerIP);
562                           PeerName0 when is_atom(PeerName0) ->
563                               atom_to_list(PeerName0);
564                           PeerName0 when is_list(PeerName0) ->
565                               PeerName0
566                       end,
567            S1 =
568                S0#ssh{c_vsn = Vsn,
569                       c_version = Version,
570                       opts = ?PUT_INTERNAL_OPT({io_cb, case ?GET_OPT(user_interaction, Opts) of
571                                                            true ->  ssh_io;
572                                                            false -> ssh_no_io
573                                                        end},
574                                                Opts),
575                       userauth_quiet_mode = ?GET_OPT(quiet_mode, Opts),
576                       peer = {PeerName, PeerAddr},
577                       local = LocalName
578                      },
579            S1#ssh{userauth_pubkeys = [K || K <- ?GET_OPT(pref_public_key_algs, Opts),
580                                            is_usable_user_pubkey(K, S1)
581                                      ]
582                  };
583
584	server ->
585	    S0#ssh{s_vsn = Vsn,
586		   s_version = Version,
587		   userauth_methods = string:tokens(AuthMethods, ","),
588		   kb_tries_left = 3,
589		   peer = {undefined, PeerAddr},
590                   local = LocalName
591		  }
592    end.
593
594
595
596%%====================================================================
597%% gen_statem callbacks
598%%====================================================================
599%%--------------------------------------------------------------------
600-type event_content() ::  any().
601
602-type renegotiate_flag() :: init | renegotiate.
603
604-type state_name() ::
605        {hello,                     role()                    }
606      | {kexinit,                   role(), renegotiate_flag()}
607      | {key_exchange,              role(), renegotiate_flag()}
608      | {key_exchange_dh_gex_init,  server, renegotiate_flag()}
609      | {key_exchange_dh_gex_reply, client, renegotiate_flag()}
610      | {new_keys,                  role(), renegotiate_flag()}
611      | {ext_info,                  role(), renegotiate_flag()}
612      | {service_request,           role()                    }
613      | {userauth,                  role()                    }
614      | {userauth_keyboard_interactive,       role()          }
615      | {userauth_keyboard_interactive_extra, server          }
616      | {userauth_keyboard_interactive_info_response, client  }
617      | {connected,                 role()                    }
618	.
619
620%% The state names must fulfill some rules regarding
621%% where the role() and the renegotiate_flag() is placed:
622
623-spec role(state_name()) -> role().
624role({_,Role}) -> Role;
625role({_,Role,_}) -> Role.
626
627-spec renegotiation(state_name()) -> boolean().
628renegotiation({_,_,ReNeg}) -> ReNeg == renegotiate;
629renegotiation(_) -> false.
630
631
632-define(CONNECTED(StateName),
633        (element(1,StateName) == connected orelse
634         element(1,StateName) == ext_info ) ).
635
636-spec handle_event(gen_statem:event_type(),
637		   event_content(),
638		   state_name(),
639		   #data{}
640		  ) -> gen_statem:event_handler_result(state_name()) .
641
642-define(CONNECTION_MSG(Msg),
643        [{next_event, internal, prepare_next_packet},
644         {next_event,internal,{conn_msg,Msg}}]).
645
646%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
647
648callback_mode() ->
649    [handle_event_function,
650     state_enter].
651
652
653handle_event(_, _Event, {init_error,Error}=StateName, D) ->
654    case Error of
655        enotconn ->
656           %% Handles the abnormal sequence:
657           %%    SYN->
658           %%            <-SYNACK
659           %%    ACK->
660           %%    RST->
661            ?call_disconnectfun_and_log_cond("Protocol Error",
662                                             "TCP connenction to server was prematurely closed by the client",
663                                             StateName, D),
664            {stop, {shutdown,"TCP connenction to server was prematurely closed by the client"}};
665
666        OtherError ->
667            {stop, {shutdown,{init,OtherError}}}
668    end;
669
670%%% ######## {hello, client|server} ####
671%% The very first event that is sent when the we are set as controlling process of Socket
672handle_event(_, socket_control, {hello,_}=StateName, #data{ssh_params = Ssh0} = D) ->
673    VsnMsg = ssh_transport:hello_version_msg(string_version(Ssh0)),
674    send_bytes(VsnMsg, D),
675    case inet:getopts(Socket=D#data.socket, [recbuf]) of
676	{ok, [{recbuf,Size}]} ->
677	    %% Set the socket to the hello text line handling mode:
678	    inet:setopts(Socket, [{packet, line},
679				  {active, once},
680				  % Expecting the version string which might
681				  % be max ?MAX_PROTO_VERSION bytes:
682				  {recbuf, ?MAX_PROTO_VERSION},
683				  {nodelay,true}]),
684            Time = ?GET_OPT(hello_timeout, Ssh0#ssh.opts, infinity),
685	    {keep_state, D#data{inet_initial_recbuf_size=Size}, [{state_timeout,Time,no_hello_received}] };
686
687	Other ->
688            ?call_disconnectfun_and_log_cond("Option return",
689                                             io_lib:format("Unexpected getopts return:~n  ~p",[Other]),
690                                             StateName, D),
691	    {stop, {shutdown,{unexpected_getopts_return, Other}}}
692    end;
693
694handle_event(_, {info_line,Line}, {hello,Role}=StateName, D) ->
695    case Role of
696	client ->
697	    %% The server may send info lines to the client before the version_exchange
698	    %% RFC4253/4.2
699	    inet:setopts(D#data.socket, [{active, once}]),
700	    keep_state_and_data;
701	server ->
702	    %% But the client may NOT send them to the server. Openssh answers with cleartext,
703	    %% and so do we
704	    send_bytes("Protocol mismatch.", D),
705            Msg = io_lib:format("Protocol mismatch in version exchange. Client sent info lines.~n~s",
706                                [ssh_dbg:hex_dump(Line, 64)]),
707            ?call_disconnectfun_and_log_cond("Protocol mismatch.", Msg, StateName, D),
708	    {stop, {shutdown,"Protocol mismatch in version exchange. Client sent info lines."}}
709    end;
710
711handle_event(_, {version_exchange,Version}, {hello,Role}, D0) ->
712    {NumVsn, StrVsn} = ssh_transport:handle_hello_version(Version),
713    case handle_version(NumVsn, StrVsn, D0#data.ssh_params) of
714	{ok, Ssh1} ->
715	    %% Since the hello part is finnished correctly, we set the
716	    %% socket to the packet handling mode (including recbuf size):
717	    inet:setopts(D0#data.socket, [{packet,0},
718					 {mode,binary},
719					 {active, once},
720					 {recbuf, D0#data.inet_initial_recbuf_size}]),
721	    {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(Ssh1),
722	    send_bytes(SshPacket, D0),
723	    {next_state, {kexinit,Role,init}, D0#data{ssh_params = Ssh,
724						     key_exchange_init_msg = KeyInitMsg}};
725	not_supported ->
726            {Shutdown, D} =
727                ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED,
728                                 io_lib:format("Offending version is ~p",[string:chomp(Version)]),
729                                 {hello,Role},
730                                 D0),
731	    {stop, Shutdown, D}
732    end;
733
734handle_event(_, no_hello_received, {hello,_Role}=StateName, D0) ->
735    {Shutdown, D} =
736        ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR, "No HELLO recieved", StateName, D0),
737    {stop, Shutdown, D};
738
739%%% ######## {kexinit, client|server, init|renegotiate} ####
740
741handle_event(_, {#ssh_msg_kexinit{}=Kex, Payload}, {kexinit,Role,ReNeg},
742	     D = #data{key_exchange_init_msg = OwnKex}) ->
743    Ssh1 = ssh_transport:key_init(peer_role(Role), D#data.ssh_params, Payload),
744    Ssh = case ssh_transport:handle_kexinit_msg(Kex, OwnKex, Ssh1) of
745	      {ok, NextKexMsg, Ssh2} when Role==client ->
746		  send_bytes(NextKexMsg, D),
747		  Ssh2;
748	      {ok, Ssh2} when Role==server ->
749		  Ssh2
750	  end,
751    {next_state, {key_exchange,Role,ReNeg}, D#data{ssh_params=Ssh}};
752
753
754%%% ######## {key_exchange, client|server, init|renegotiate} ####
755
756%%%---- diffie-hellman
757handle_event(_, #ssh_msg_kexdh_init{} = Msg, {key_exchange,server,ReNeg}, D) ->
758    {ok, KexdhReply, Ssh1} = ssh_transport:handle_kexdh_init(Msg, D#data.ssh_params),
759    send_bytes(KexdhReply, D),
760    {ok, NewKeys, Ssh2} = ssh_transport:new_keys_message(Ssh1),
761    send_bytes(NewKeys, D),
762    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh2),
763    send_bytes(ExtInfo, D),
764    {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
765
766handle_event(_, #ssh_msg_kexdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) ->
767    {ok, NewKeys, Ssh1} = ssh_transport:handle_kexdh_reply(Msg, D#data.ssh_params),
768    send_bytes(NewKeys, D),
769    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
770    send_bytes(ExtInfo, D),
771    {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
772
773%%%---- diffie-hellman group exchange
774handle_event(_, #ssh_msg_kex_dh_gex_request{} = Msg, {key_exchange,server,ReNeg}, D) ->
775    {ok, GexGroup, Ssh1} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
776    send_bytes(GexGroup, D),
777    Ssh = ssh_transport:parallell_gen_key(Ssh1),
778    {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}};
779
780handle_event(_, #ssh_msg_kex_dh_gex_request_old{} = Msg, {key_exchange,server,ReNeg}, D) ->
781    {ok, GexGroup, Ssh1} = ssh_transport:handle_kex_dh_gex_request(Msg, D#data.ssh_params),
782    send_bytes(GexGroup, D),
783    Ssh = ssh_transport:parallell_gen_key(Ssh1),
784    {next_state, {key_exchange_dh_gex_init,server,ReNeg}, D#data{ssh_params=Ssh}};
785
786handle_event(_, #ssh_msg_kex_dh_gex_group{} = Msg, {key_exchange,client,ReNeg}, D) ->
787    {ok, KexGexInit, Ssh} = ssh_transport:handle_kex_dh_gex_group(Msg, D#data.ssh_params),
788    send_bytes(KexGexInit, D),
789    {next_state, {key_exchange_dh_gex_reply,client,ReNeg}, D#data{ssh_params=Ssh}};
790
791%%%---- elliptic curve diffie-hellman
792handle_event(_, #ssh_msg_kex_ecdh_init{} = Msg, {key_exchange,server,ReNeg}, D) ->
793    {ok, KexEcdhReply, Ssh1} = ssh_transport:handle_kex_ecdh_init(Msg, D#data.ssh_params),
794    send_bytes(KexEcdhReply, D),
795    {ok, NewKeys, Ssh2} = ssh_transport:new_keys_message(Ssh1),
796    send_bytes(NewKeys, D),
797    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh2),
798    send_bytes(ExtInfo, D),
799    {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
800
801handle_event(_, #ssh_msg_kex_ecdh_reply{} = Msg, {key_exchange,client,ReNeg}, D) ->
802    {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_ecdh_reply(Msg, D#data.ssh_params),
803    send_bytes(NewKeys, D),
804    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
805    send_bytes(ExtInfo, D),
806    {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
807
808
809%%% ######## {key_exchange_dh_gex_init, server, init|renegotiate} ####
810
811handle_event(_, #ssh_msg_kex_dh_gex_init{} = Msg, {key_exchange_dh_gex_init,server,ReNeg}, D) ->
812    {ok, KexGexReply, Ssh1} =  ssh_transport:handle_kex_dh_gex_init(Msg, D#data.ssh_params),
813    send_bytes(KexGexReply, D),
814    {ok, NewKeys, Ssh2} = ssh_transport:new_keys_message(Ssh1),
815    send_bytes(NewKeys, D),
816    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh2),
817    send_bytes(ExtInfo, D),
818    {next_state, {new_keys,server,ReNeg}, D#data{ssh_params=Ssh}};
819
820
821%%% ######## {key_exchange_dh_gex_reply, client, init|renegotiate} ####
822
823handle_event(_, #ssh_msg_kex_dh_gex_reply{} = Msg, {key_exchange_dh_gex_reply,client,ReNeg}, D) ->
824    {ok, NewKeys, Ssh1} = ssh_transport:handle_kex_dh_gex_reply(Msg, D#data.ssh_params),
825    send_bytes(NewKeys, D),
826    {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
827    send_bytes(ExtInfo, D),
828    {next_state, {new_keys,client,ReNeg}, D#data{ssh_params=Ssh}};
829
830
831%%% ######## {new_keys, client|server} ####
832
833%% First key exchange round:
834handle_event(_, #ssh_msg_newkeys{} = Msg, {new_keys,client,init}, D0) ->
835    {ok, Ssh1} = ssh_transport:handle_new_keys(Msg, D0#data.ssh_params),
836    %% {ok, ExtInfo, Ssh2} = ssh_transport:ext_info_message(Ssh1),
837    %% send_bytes(ExtInfo, D0),
838    {MsgReq, Ssh} = ssh_auth:service_request_msg(Ssh1),
839    D = send_msg(MsgReq, D0#data{ssh_params = Ssh}),
840    {next_state, {ext_info,client,init}, D};
841
842handle_event(_, #ssh_msg_newkeys{} = Msg, {new_keys,server,init}, D) ->
843    {ok, Ssh} = ssh_transport:handle_new_keys(Msg, D#data.ssh_params),
844    %% {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
845    %% send_bytes(ExtInfo, D),
846    {next_state, {ext_info,server,init}, D#data{ssh_params=Ssh}};
847
848%% Subsequent key exchange rounds (renegotiation):
849handle_event(_, #ssh_msg_newkeys{} = Msg, {new_keys,Role,renegotiate}, D) ->
850    {ok, Ssh} = ssh_transport:handle_new_keys(Msg, D#data.ssh_params),
851    %% {ok, ExtInfo, Ssh} = ssh_transport:ext_info_message(Ssh1),
852    %% send_bytes(ExtInfo, D),
853    {next_state, {ext_info,Role,renegotiate}, D#data{ssh_params=Ssh}};
854
855
856%%% ######## {ext_info, client|server, init|renegotiate} ####
857
858handle_event(_, #ssh_msg_ext_info{}=Msg, {ext_info,Role,init}, D0) ->
859    D = handle_ssh_msg_ext_info(Msg, D0),
860    {next_state, {service_request,Role}, D};
861
862handle_event(_, #ssh_msg_ext_info{}=Msg, {ext_info,Role,renegotiate}, D0) ->
863    D = handle_ssh_msg_ext_info(Msg, D0),
864    {next_state, {connected,Role}, D};
865
866handle_event(_, #ssh_msg_newkeys{}=Msg, {ext_info,_Role,renegotiate}, D) ->
867    {ok, Ssh} = ssh_transport:handle_new_keys(Msg, D#data.ssh_params),
868    {keep_state, D#data{ssh_params = Ssh}};
869
870
871handle_event(internal, Msg, {ext_info,Role,init}, D) when is_tuple(Msg) ->
872    %% If something else arrives, goto next state and handle the event in that one
873    {next_state, {service_request,Role}, D, [postpone]};
874
875handle_event(internal, Msg, {ext_info,Role,_ReNegFlag}, D) when is_tuple(Msg) ->
876    %% If something else arrives, goto next state and handle the event in that one
877    {next_state, {connected,Role}, D, [postpone]};
878
879%%% ######## {service_request, client|server} ####
880
881handle_event(_, Msg = #ssh_msg_service_request{name=ServiceName}, StateName = {service_request,server}, D0) ->
882    case ServiceName of
883	"ssh-userauth" ->
884	    Ssh0 = #ssh{session_id=SessionId} = D0#data.ssh_params,
885	    {ok, {Reply, Ssh}} = ssh_auth:handle_userauth_request(Msg, SessionId, Ssh0),
886            D = send_msg(Reply, D0#data{ssh_params = Ssh}),
887	    {next_state, {userauth,server}, D};
888
889	_ ->
890            {Shutdown, D} =
891                ?send_disconnect(?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
892                                 io_lib:format("Unknown service: ~p",[ServiceName]),
893                                 StateName, D0),
894            {stop, Shutdown, D}
895    end;
896
897handle_event(_, #ssh_msg_service_accept{name = "ssh-userauth"}, {service_request,client},
898	     #data{ssh_params = #ssh{service="ssh-userauth"} = Ssh0} = D0) ->
899    {Msg, Ssh} = ssh_auth:init_userauth_request_msg(Ssh0),
900    D = send_msg(Msg, D0#data{ssh_params = Ssh,
901                              auth_user = Ssh#ssh.user
902                             }),
903    {next_state, {userauth,client}, D};
904
905
906%%% ######## {userauth, client|server} ####
907
908%%---- userauth request to server
909handle_event(_,
910	     Msg = #ssh_msg_userauth_request{service = ServiceName, method = Method},
911	     StateName = {userauth,server},
912	     D0 = #data{ssh_params=Ssh0}) ->
913
914    case {ServiceName, Ssh0#ssh.service, Method} of
915	{"ssh-connection", "ssh-connection", "none"} ->
916	    %% Probably the very first userauth_request but we deny unauthorized login
917	    {not_authorized, _, {Reply,Ssh}} =
918		ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0),
919            D = send_msg(Reply, D0#data{ssh_params = Ssh}),
920	    {keep_state, D};
921
922	{"ssh-connection", "ssh-connection", Method} ->
923	    %% Userauth request with a method like "password" or so
924	    case lists:member(Method, Ssh0#ssh.userauth_methods) of
925		true ->
926		    %% Yepp! we support this method
927		    case ssh_auth:handle_userauth_request(Msg, Ssh0#ssh.session_id, Ssh0) of
928			{authorized, User, {Reply, Ssh1}} ->
929                            D = #data{ssh_params=Ssh} =
930                                send_msg(Reply, D0#data{ssh_params = Ssh1}),
931			    D#data.starter ! ssh_connected,
932			    connected_fun(User, Method, D),
933			    {next_state, {connected,server},
934                             D#data{auth_user=User,
935                                    %% Note: authenticated=true MUST NOT be sent
936                                    %% before send_msg!
937                                    ssh_params = Ssh#ssh{authenticated = true}}};
938			{not_authorized, {User, Reason}, {Reply, Ssh}} when Method == "keyboard-interactive" ->
939			    retry_fun(User, Reason, D0),
940                            D = send_msg(Reply, D0#data{ssh_params = Ssh}),
941			    {next_state, {userauth_keyboard_interactive,server}, D};
942			{not_authorized, {User, Reason}, {Reply, Ssh}} ->
943			    retry_fun(User, Reason, D0),
944                            D = send_msg(Reply, D0#data{ssh_params = Ssh}),
945			    {keep_state, D}
946		    end;
947		false ->
948		    %% No we do not support this method (=/= none)
949		    %% At least one non-erlang client does like this. Retry as the next event
950		    {keep_state_and_data,
951		     [{next_event, internal, Msg#ssh_msg_userauth_request{method="none"}}]
952		    }
953	    end;
954
955	%% {"ssh-connection", Expected, Method} when Expected =/= ServiceName -> Do what?
956	%% {ServiceName,      Expected, Method} when Expected =/= ServiceName -> Do what?
957
958	{ServiceName, _, _} when ServiceName =/= "ssh-connection" ->
959            {Shutdown, D} =
960                ?send_disconnect(?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE,
961                                 io_lib:format("Unknown service: ~p",[ServiceName]),
962                                 StateName, D0),
963            {stop, Shutdown, D}
964    end;
965
966%%---- userauth success to client
967handle_event(_, #ssh_msg_ext_info{}=Msg, {userauth,client}, D0) ->
968    %% FIXME: need new state to receive this msg!
969    D = handle_ssh_msg_ext_info(Msg, D0),
970    {keep_state, D};
971
972handle_event(_, #ssh_msg_userauth_success{}, {userauth,client}, D=#data{ssh_params = Ssh}) ->
973    ssh_auth:ssh_msg_userauth_result(success),
974    D#data.starter ! ssh_connected,
975    {next_state, {connected,client}, D#data{ssh_params=Ssh#ssh{authenticated = true}}};
976
977
978%%---- userauth failure response to client
979handle_event(_, #ssh_msg_userauth_failure{}, {userauth,client}=StateName,
980	     #data{ssh_params = #ssh{userauth_methods = []}} = D0) ->
981    {Shutdown, D} =
982        ?send_disconnect(?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE,
983                         io_lib:format("User auth failed for: ~p",[D0#data.auth_user]),
984                         StateName, D0),
985    {stop, Shutdown, D};
986handle_event(_, #ssh_msg_userauth_failure{authentications = Methods}, StateName={userauth,client},
987	     D0 = #data{ssh_params = Ssh0}) ->
988    %% The prefered authentication method failed try next method
989    Ssh1 = case Ssh0#ssh.userauth_methods of
990	       none ->
991		   %% Server tells us which authentication methods that are allowed
992		   Ssh0#ssh{userauth_methods = string:tokens(Methods, ",")};
993	       _ ->
994		   %% We already know...
995		   Ssh0
996	   end,
997    case ssh_auth:userauth_request_msg(Ssh1) of
998        {send_disconnect, Code, Ssh} ->
999            {Shutdown, D} =
1000                ?send_disconnect(Code,
1001                                 io_lib:format("User auth failed for: ~p",[D0#data.auth_user]),
1002                                 StateName, D0#data{ssh_params = Ssh}),
1003	    {stop, Shutdown, D};
1004	{"keyboard-interactive", {Msg, Ssh}} ->
1005            D = send_msg(Msg, D0#data{ssh_params = Ssh}),
1006	    {next_state, {userauth_keyboard_interactive,client}, D};
1007	{_Method, {Msg, Ssh}} ->
1008            D = send_msg(Msg, D0#data{ssh_params = Ssh}),
1009	    {keep_state, D}
1010    end;
1011
1012%%---- banner to client
1013handle_event(_, #ssh_msg_userauth_banner{message = Msg}, {userauth,client}, D) ->
1014    case D#data.ssh_params#ssh.userauth_quiet_mode of
1015	false -> io:format("~s", [Msg]);
1016	true -> ok
1017    end,
1018    keep_state_and_data;
1019
1020
1021%%% ######## {userauth_keyboard_interactive, client|server}
1022
1023handle_event(_, #ssh_msg_userauth_info_request{} = Msg, {userauth_keyboard_interactive, client},
1024	     #data{ssh_params = Ssh0} = D0) ->
1025    case ssh_auth:handle_userauth_info_request(Msg, Ssh0) of
1026	{ok, {Reply, Ssh}} ->
1027            D = send_msg(Reply, D0#data{ssh_params = Ssh}),
1028	    {next_state, {userauth_keyboard_interactive_info_response,client}, D};
1029	not_ok ->
1030	    {next_state, {userauth,client}, D0, [postpone]}
1031    end;
1032
1033handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive, server}, D0) ->
1034    case ssh_auth:handle_userauth_info_response(Msg, D0#data.ssh_params) of
1035	{authorized, User, {Reply, Ssh1}} ->
1036            D = #data{ssh_params=Ssh} =
1037                send_msg(Reply, D0#data{ssh_params = Ssh1}),
1038	    D#data.starter ! ssh_connected,
1039	    connected_fun(User, "keyboard-interactive", D),
1040	    {next_state, {connected,server}, D#data{auth_user = User,
1041                                                    %% Note: authenticated=true MUST NOT be sent
1042                                                    %% before send_msg!
1043						    ssh_params = Ssh#ssh{authenticated = true}}};
1044	{not_authorized, {User, Reason}, {Reply, Ssh}} ->
1045	    retry_fun(User, Reason, D0),
1046            D = send_msg(Reply, D0#data{ssh_params = Ssh}),
1047	    {next_state, {userauth,server}, D};
1048
1049	{authorized_but_one_more, _User,  {Reply, Ssh}} ->
1050            D = send_msg(Reply, D0#data{ssh_params = Ssh}),
1051	    {next_state, {userauth_keyboard_interactive_extra,server}, D}
1052    end;
1053
1054handle_event(_, #ssh_msg_userauth_info_response{} = Msg, {userauth_keyboard_interactive_extra, server}, D0) ->
1055    {authorized, User, {Reply, Ssh1}} =
1056        ssh_auth:handle_userauth_info_response({extra,Msg}, D0#data.ssh_params),
1057    D = #data{ssh_params=Ssh} =
1058        send_msg(Reply, D0#data{ssh_params = Ssh1}),
1059    D#data.starter ! ssh_connected,
1060    connected_fun(User, "keyboard-interactive", D),
1061    {next_state, {connected,server}, D#data{auth_user = User,
1062                                            %% Note: authenticated=true MUST NOT be sent
1063                                            %% before send_msg!
1064					    ssh_params = Ssh#ssh{authenticated = true}}};
1065
1066handle_event(_, #ssh_msg_userauth_failure{}, {userauth_keyboard_interactive, client},
1067	     #data{ssh_params = Ssh0} = D0) ->
1068    Prefs = [{Method,M,F,A} || {Method,M,F,A} <- Ssh0#ssh.userauth_preference,
1069			       Method =/= "keyboard-interactive"],
1070    D = D0#data{ssh_params = Ssh0#ssh{userauth_preference=Prefs}},
1071    {next_state, {userauth,client}, D, [postpone]};
1072
1073handle_event(_, #ssh_msg_userauth_failure{}, {userauth_keyboard_interactive_info_response, client},
1074	     #data{ssh_params = Ssh0} = D0) ->
1075    Opts = Ssh0#ssh.opts,
1076    D = case ?GET_OPT(password, Opts) of
1077	    undefined ->
1078		D0;
1079	    _ ->
1080		D0#data{ssh_params =
1081			    Ssh0#ssh{opts = ?PUT_OPT({password,not_ok}, Opts)}} % FIXME:intermodule dependency
1082	end,
1083    {next_state, {userauth,client}, D, [postpone]};
1084
1085handle_event(_, #ssh_msg_ext_info{}=Msg, {userauth_keyboard_interactive_info_response, client}, D0) ->
1086    %% FIXME: need new state to receive this msg!
1087    D = handle_ssh_msg_ext_info(Msg, D0),
1088    {keep_state, D};
1089
1090handle_event(_, #ssh_msg_userauth_success{}, {userauth_keyboard_interactive_info_response, client}, D) ->
1091    {next_state, {userauth,client}, D, [postpone]};
1092
1093handle_event(_, #ssh_msg_userauth_info_request{}, {userauth_keyboard_interactive_info_response, client}, D) ->
1094    {next_state, {userauth_keyboard_interactive,client}, D, [postpone]};
1095
1096
1097%%% ######## {connected, client|server} ####
1098
1099%% Skip ext_info messages in connected state (for example from OpenSSH >= 7.7)
1100handle_event(_, #ssh_msg_ext_info{}, {connected,_Role}, D) ->
1101    {keep_state, D};
1102
1103handle_event(_, {#ssh_msg_kexinit{},_}, {connected,Role}, D0) ->
1104    {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(D0#data.ssh_params),
1105    D = D0#data{ssh_params = Ssh,
1106		key_exchange_init_msg = KeyInitMsg},
1107    send_bytes(SshPacket, D),
1108    {next_state, {kexinit,Role,renegotiate}, D, [postpone]};
1109
1110handle_event(_, #ssh_msg_disconnect{description=Desc} = Msg, StateName, D0) ->
1111    {disconnect, _, RepliesCon} =
1112	ssh_connection:handle_msg(Msg, D0#data.connection_state, role(StateName), D0#data.ssh_params),
1113    {Actions,D} = send_replies(RepliesCon, D0),
1114    disconnect_fun("Received disconnect: "++Desc, D),
1115    {stop_and_reply, {shutdown,Desc}, Actions, D};
1116
1117handle_event(_, #ssh_msg_ignore{}, _, _) ->
1118    keep_state_and_data;
1119
1120handle_event(_, #ssh_msg_unimplemented{}, _, _) ->
1121    keep_state_and_data;
1122
1123handle_event(_, #ssh_msg_debug{} = Msg, _, D) ->
1124    debug_fun(Msg, D),
1125    keep_state_and_data;
1126
1127handle_event(internal, {conn_msg,Msg}, StateName, #data{starter = User,
1128                                                        connection_state = Connection0,
1129                                                        event_queue = Qev0} = D0) ->
1130    Role = role(StateName),
1131    Rengotation = renegotiation(StateName),
1132    try ssh_connection:handle_msg(Msg, Connection0, Role, D0#data.ssh_params) of
1133	{disconnect, Reason0, RepliesConn} ->
1134            {Repls, D} = send_replies(RepliesConn, D0),
1135            case {Reason0,Role} of
1136                {{_, Reason}, client} when ((StateName =/= {connected,client})
1137                                            and (not Rengotation)) ->
1138		   User ! {self(), not_connected, Reason};
1139                _ ->
1140                    ok
1141            end,
1142            {stop_and_reply, {shutdown,normal}, Repls, D};
1143
1144	{Replies, Connection} when is_list(Replies) ->
1145	    {Repls, D} =
1146		case StateName of
1147		    {connected,_} ->
1148			send_replies(Replies, D0#data{connection_state=Connection});
1149		    _ ->
1150			{ConnReplies, NonConnReplies} = lists:splitwith(fun not_connected_filter/1, Replies),
1151			send_replies(NonConnReplies, D0#data{event_queue = Qev0 ++ ConnReplies})
1152		end,
1153            case {Msg, StateName} of
1154                {#ssh_msg_channel_close{}, {connected,_}} ->
1155                    {keep_state, D, [cond_set_idle_timer(D)|Repls]};
1156                {#ssh_msg_channel_success{}, _} ->
1157                    update_inet_buffers(D#data.socket),
1158                    {keep_state, D, Repls};
1159                _ ->
1160                    {keep_state, D, Repls}
1161            end
1162
1163    catch
1164	Class:Error ->
1165            {Repls, D1} = send_replies(ssh_connection:handle_stop(Connection0), D0),
1166            {Shutdown, D} = ?send_disconnect(?SSH_DISCONNECT_BY_APPLICATION,
1167                                             io_lib:format("Internal error: ~p:~p",[Class,Error]),
1168                                             StateName, D1),
1169            {stop_and_reply, Shutdown, Repls, D}
1170    end;
1171
1172
1173handle_event(enter, OldState, {connected,_}=NewState, D) ->
1174    %% Entering the state where re-negotiation is possible
1175    init_renegotiate_timers(OldState, NewState, D);
1176
1177handle_event(enter, OldState, {ext_info,_,renegotiate}=NewState, D) ->
1178    %% Could be hanging in exit_info state if nothing else arrives
1179    init_renegotiate_timers(OldState, NewState, D);
1180
1181handle_event(enter, {connected,_}=OldState, NewState, D) ->
1182    %% Exiting the state where re-negotiation is possible
1183    pause_renegotiate_timers(OldState, NewState, D);
1184
1185handle_event(cast, force_renegotiate, StateName, D) ->
1186    handle_event({timeout,renegotiate}, undefined, StateName, D);
1187
1188handle_event({timeout,renegotiate}, _, StateName, D0) ->
1189    case StateName of
1190        {connected,Role} ->
1191            start_rekeying(Role, D0);
1192        {ext_info,Role,renegotiate} ->
1193            start_rekeying(Role, D0);
1194        _ ->
1195            %% Wrong state for starting a renegotiation, must be in re-negotiation
1196            keep_state_and_data
1197    end;
1198
1199handle_event({timeout,check_data_size}, _, StateName, D0) ->
1200    %% Rekey due to sent data limit reached? (Can't be in {ext_info,...} if data is sent)
1201    case StateName of
1202        {connected,Role} ->
1203            check_data_rekeying(Role, D0);
1204        _ ->
1205            %% Wrong state for starting a renegotiation, must be in re-negotiation
1206            keep_state_and_data
1207    end;
1208
1209handle_event({call,From}, get_alg, _, D) ->
1210    #ssh{algorithms=Algs} = D#data.ssh_params,
1211    {keep_state_and_data, [{reply,From,Algs}]};
1212
1213handle_event(cast, _, StateName, _) when not ?CONNECTED(StateName) ->
1214    {keep_state_and_data, [postpone]};
1215
1216handle_event(cast, {adjust_window,ChannelId,Bytes}, StateName, D) when ?CONNECTED(StateName) ->
1217    case ssh_client_channel:cache_lookup(cache(D), ChannelId) of
1218	#channel{recv_window_size = WinSize,
1219		 recv_window_pending = Pending,
1220		 recv_packet_size = PktSize} = Channel
1221	  when (WinSize-Bytes) >= 2*PktSize ->
1222	    %% The peer can send at least two more *full* packet, no hurry.
1223	    ssh_client_channel:cache_update(cache(D),
1224				     Channel#channel{recv_window_pending = Pending + Bytes}),
1225	    keep_state_and_data;
1226
1227	#channel{recv_window_size = WinSize,
1228		 recv_window_pending = Pending,
1229		 remote_id = Id} = Channel ->
1230	    %% Now we have to update the window - we can't receive so many more pkts
1231	    ssh_client_channel:cache_update(cache(D),
1232				     Channel#channel{recv_window_size =
1233							 WinSize + Bytes + Pending,
1234						     recv_window_pending = 0}),
1235	    Msg = ssh_connection:channel_adjust_window_msg(Id, Bytes + Pending),
1236	    {keep_state, send_msg(Msg,D)};
1237
1238	undefined ->
1239	    keep_state_and_data
1240    end;
1241
1242handle_event(cast, {reply_request,Resp,ChannelId}, StateName, D) when ?CONNECTED(StateName) ->
1243    case ssh_client_channel:cache_lookup(cache(D), ChannelId) of
1244        #channel{remote_id = RemoteId} when Resp== success ; Resp==failure ->
1245            Msg =
1246                case Resp of
1247                    success -> ssh_connection:channel_success_msg(RemoteId);
1248                    failure -> ssh_connection:channel_failure_msg(RemoteId)
1249                end,
1250            update_inet_buffers(D#data.socket),
1251            {keep_state, send_msg(Msg,D)};
1252
1253        #channel{} ->
1254            Details = io_lib:format("Unhandled reply in state ~p:~n~p", [StateName,Resp]),
1255            ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR, Details, StateName, D);
1256
1257	undefined ->
1258	    keep_state_and_data
1259    end;
1260
1261handle_event(cast, {request,ChannelPid, ChannelId, Type, Data}, StateName, D) when ?CONNECTED(StateName) ->
1262    {keep_state,  handle_request(ChannelPid, ChannelId, Type, Data, false, none, D)};
1263
1264handle_event(cast, {request,ChannelId,Type,Data}, StateName, D) when ?CONNECTED(StateName) ->
1265    {keep_state,  handle_request(ChannelId, Type, Data, false, none, D)};
1266
1267handle_event(cast, {unknown,Data}, StateName, D) when ?CONNECTED(StateName) ->
1268    Msg = #ssh_msg_unimplemented{sequence = Data},
1269    {keep_state, send_msg(Msg,D)};
1270
1271handle_event(cast, {global_request, Type, Data}, StateName, D) when ?CONNECTED(StateName) ->
1272    {keep_state, send_msg(ssh_connection:request_global_msg(Type,false,Data), D)};
1273
1274
1275%%% Previously handle_sync_event began here
1276handle_event({call,From}, get_print_info, StateName, D) ->
1277    Reply =
1278	try
1279	    {inet:sockname(D#data.socket),
1280	     inet:peername(D#data.socket)
1281	    }
1282	of
1283	    {{ok,Local}, {ok,Remote}} ->
1284		{{Local,Remote},io_lib:format("statename=~p",[StateName])};
1285	    _ ->
1286		{{"-",0},"-"}
1287	catch
1288	    _:_ ->
1289		{{"?",0},"?"}
1290	end,
1291    {keep_state_and_data, [{reply,From,Reply}]};
1292
1293handle_event({call,From}, {connection_info, Options}, _, D) ->
1294    Info = fold_keys(Options, fun conn_info/2, D),
1295    {keep_state_and_data, [{reply,From,Info}]};
1296
1297handle_event({call,From}, {channel_info,ChannelId,Options}, _, D) ->
1298    case ssh_client_channel:cache_lookup(cache(D), ChannelId) of
1299	#channel{} = Channel ->
1300	    Info = fold_keys(Options, fun chann_info/2, Channel),
1301	    {keep_state_and_data, [{reply,From,Info}]};
1302	undefined ->
1303	    {keep_state_and_data, [{reply,From,[]}]}
1304    end;
1305
1306
1307handle_event({call,From}, {info, all}, _, D) ->
1308    Result = ssh_client_channel:cache_foldl(fun(Channel, Acc) ->
1309					     [Channel | Acc]
1310				     end,
1311				     [], cache(D)),
1312    {keep_state_and_data, [{reply, From, {ok,Result}}]};
1313
1314handle_event({call,From}, {info, ChannelPid}, _, D) ->
1315    Result = ssh_client_channel:cache_foldl(
1316	       fun(Channel, Acc) when Channel#channel.user == ChannelPid ->
1317		       [Channel | Acc];
1318		  (_, Acc) ->
1319		       Acc
1320	       end, [], cache(D)),
1321    {keep_state_and_data, [{reply, From, {ok,Result}}]};
1322
1323handle_event({call,From}, {set_sock_opts,SocketOptions}, _StateName, D) ->
1324    Result = try inet:setopts(D#data.socket, SocketOptions)
1325             catch
1326                 _:_ -> {error, badarg}
1327             end,
1328    {keep_state_and_data, [{reply,From,Result}]};
1329
1330handle_event({call,From}, {get_sock_opts,SocketGetOptions}, _StateName, D) ->
1331    Result = try inet:getopts(D#data.socket, SocketGetOptions)
1332             catch
1333                 _:_ -> {error, badarg}
1334             end,
1335    {keep_state_and_data, [{reply,From,Result}]};
1336
1337handle_event({call,From}, stop, _StateName, D0) ->
1338    {Repls,D} = send_replies(ssh_connection:handle_stop(D0#data.connection_state), D0),
1339    {stop_and_reply, normal, [{reply,From,ok}|Repls], D};
1340
1341handle_event({call,_}, _, StateName, _) when not ?CONNECTED(StateName) ->
1342    {keep_state_and_data, [postpone]};
1343
1344handle_event({call,From}, {request, ChannelPid, ChannelId, Type, Data, Timeout}, StateName, D0)
1345  when ?CONNECTED(StateName) ->
1346    case handle_request(ChannelPid, ChannelId, Type, Data, true, From, D0) of
1347        {error,Error} ->
1348            {keep_state, D0, {reply,From,{error,Error}}};
1349        D ->
1350            %% Note reply to channel will happen later when reply is recived from peer on the socket
1351            start_channel_request_timer(ChannelId, From, Timeout),
1352            {keep_state, D, cond_set_idle_timer(D)}
1353    end;
1354
1355handle_event({call,From}, {request, ChannelId, Type, Data, Timeout}, StateName, D0)
1356  when ?CONNECTED(StateName) ->
1357    case handle_request(ChannelId, Type, Data, true, From, D0) of
1358        {error,Error} ->
1359            {keep_state, D0, {reply,From,{error,Error}}};
1360        D ->
1361            %% Note reply to channel will happen later when reply is recived from peer on the socket
1362            start_channel_request_timer(ChannelId, From, Timeout),
1363            {keep_state, D, cond_set_idle_timer(D)}
1364    end;
1365
1366handle_event({call,From}, {global_request, "tcpip-forward" = Type,
1367                           {ListenHost,ListenPort,ConnectToHost,ConnectToPort},
1368                           Timeout}, StateName, D0) when ?CONNECTED(StateName) ->
1369    Id = make_ref(),
1370    Data =  <<?STRING(ListenHost), ?Euint32(ListenPort)>>,
1371    Fun = fun({success, <<Port:32/unsigned-integer>>}, C) ->
1372                  Key = {tcpip_forward,ListenHost,Port},
1373                  Value = {ConnectToHost,ConnectToPort},
1374                  C#connection{options = ?PUT_INTERNAL_OPT({Key,Value}, C#connection.options)};
1375             ({success, <<>>}, C) ->
1376                  Key = {tcpip_forward,ListenHost,ListenPort},
1377                  Value = {ConnectToHost,ConnectToPort},
1378                  C#connection{options = ?PUT_INTERNAL_OPT({Key,Value}, C#connection.options)};
1379             (_, C) ->
1380                  C
1381          end,
1382    D = send_msg(ssh_connection:request_global_msg(Type, true, Data),
1383                 add_request(Fun, Id, From, D0)),
1384    start_channel_request_timer(Id, From, Timeout),
1385    {keep_state, D, cond_set_idle_timer(D)};
1386
1387handle_event({call,From}, {global_request, Type, Data, Timeout}, StateName, D0) when ?CONNECTED(StateName) ->
1388    Id = make_ref(),
1389    D = send_msg(ssh_connection:request_global_msg(Type, true, Data),
1390                 add_request(true, Id, From, D0)),
1391    start_channel_request_timer(Id, From, Timeout),
1392    {keep_state, D, cond_set_idle_timer(D)};
1393
1394handle_event({call,From}, {data, ChannelId, Type, Data, Timeout}, StateName, D0)
1395  when ?CONNECTED(StateName) ->
1396    {Repls,D} = send_replies(ssh_connection:channel_data(ChannelId, Type, Data, D0#data.connection_state, From),
1397                             D0),
1398    start_channel_request_timer(ChannelId, From, Timeout), % FIXME: No message exchange so why?
1399    {keep_state, D, Repls};
1400
1401handle_event({call,From}, {eof, ChannelId}, StateName, D0)
1402  when ?CONNECTED(StateName) ->
1403    case ssh_client_channel:cache_lookup(cache(D0), ChannelId) of
1404	#channel{remote_id = Id, sent_close = false} ->
1405	    D = send_msg(ssh_connection:channel_eof_msg(Id), D0),
1406	    {keep_state, D, [{reply,From,ok}]};
1407	_ ->
1408	    {keep_state, D0, [{reply,From,{error,closed}}]}
1409    end;
1410
1411handle_event({call,From}, get_misc, StateName,
1412             #data{connection_state = #connection{options = Opts}} = D) when ?CONNECTED(StateName) ->
1413    Sups = ?GET_INTERNAL_OPT(supervisors, Opts),
1414    SubSysSup = proplists:get_value(subsystem_sup,  Sups),
1415    Reply = {ok, {SubSysSup, role(StateName), Opts}},
1416    {keep_state, D, [{reply,From,Reply}]};
1417
1418handle_event({call,From},
1419	     {open, ChannelPid, Type, InitialWindowSize, MaxPacketSize, Data, Timeout},
1420	     StateName,
1421	     D0) when ?CONNECTED(StateName) ->
1422    erlang:monitor(process, ChannelPid),
1423    {ChannelId, D1} = new_channel_id(D0),
1424    D2 = send_msg(ssh_connection:channel_open_msg(Type, ChannelId,
1425						  InitialWindowSize,
1426						  MaxPacketSize, Data),
1427		  D1),
1428    ssh_client_channel:cache_update(cache(D2),
1429			     #channel{type = Type,
1430				      sys = "none",
1431				      user = ChannelPid,
1432				      local_id = ChannelId,
1433				      recv_window_size = InitialWindowSize,
1434				      recv_packet_size = MaxPacketSize,
1435				      send_buf = queue:new()
1436				     }),
1437    D = add_request(true, ChannelId, From, D2),
1438    start_channel_request_timer(ChannelId, From, Timeout),
1439    {keep_state, D, cond_set_idle_timer(D)};
1440
1441handle_event({call,From}, {send_window, ChannelId}, StateName, D)
1442  when ?CONNECTED(StateName) ->
1443    Reply = case ssh_client_channel:cache_lookup(cache(D), ChannelId) of
1444		#channel{send_window_size = WinSize,
1445			 send_packet_size = Packsize} ->
1446		    {ok, {WinSize, Packsize}};
1447		undefined ->
1448		    {error, einval}
1449	    end,
1450    {keep_state_and_data, [{reply,From,Reply}]};
1451
1452handle_event({call,From}, {recv_window, ChannelId}, StateName, D)
1453  when ?CONNECTED(StateName) ->
1454    Reply = case ssh_client_channel:cache_lookup(cache(D), ChannelId) of
1455		#channel{recv_window_size = WinSize,
1456			 recv_packet_size = Packsize} ->
1457		    {ok, {WinSize, Packsize}};
1458		undefined ->
1459		    {error, einval}
1460	    end,
1461    {keep_state_and_data, [{reply,From,Reply}]};
1462
1463handle_event({call,From}, {close, ChannelId}, StateName, D0)
1464  when ?CONNECTED(StateName) ->
1465    case ssh_client_channel:cache_lookup(cache(D0), ChannelId) of
1466	#channel{remote_id = Id} = Channel ->
1467	    D1 = send_msg(ssh_connection:channel_close_msg(Id), D0),
1468	    ssh_client_channel:cache_update(cache(D1), Channel#channel{sent_close = true}),
1469	    {keep_state, D1, [cond_set_idle_timer(D1), {reply,From,ok}]};
1470	undefined ->
1471	    {keep_state_and_data, [{reply,From,ok}]}
1472    end;
1473
1474handle_event(cast, {store,Key,Value}, _StateName, #data{connection_state=C0} = D) ->
1475    C = C0#connection{options = ?PUT_INTERNAL_OPT({Key,Value}, C0#connection.options)},
1476    {keep_state, D#data{connection_state = C}};
1477
1478handle_event({call,From}, {retrieve,Key}, _StateName, #data{connection_state=C}) ->
1479    case retrieve(C, Key) of
1480        {ok,Value} ->
1481            {keep_state_and_data, [{reply,From,{ok,Value}}]};
1482        _ ->
1483            {keep_state_and_data, [{reply,From,undefined}]}
1484    end;
1485
1486%%===== Reception of encrypted bytes, decryption and framing
1487handle_event(info, {Proto, Sock, Info}, {hello,_}, #data{socket = Sock,
1488							 transport_protocol = Proto}) ->
1489    case Info of
1490	"SSH-" ++ _ ->
1491	    {keep_state_and_data, [{next_event, internal, {version_exchange,Info}}]};
1492	_ ->
1493	    {keep_state_and_data, [{next_event, internal, {info_line,Info}}]}
1494    end;
1495
1496
1497handle_event(info, {Proto, Sock, NewData}, StateName, D0 = #data{socket = Sock,
1498								 transport_protocol = Proto}) ->
1499    try ssh_transport:handle_packet_part(
1500	  D0#data.decrypted_data_buffer,
1501	  <<(D0#data.encrypted_data_buffer)/binary, NewData/binary>>,
1502          D0#data.aead_data,
1503          D0#data.undecrypted_packet_length,
1504	  D0#data.ssh_params)
1505    of
1506	{packet_decrypted, DecryptedBytes, EncryptedDataRest, Ssh1} ->
1507	    D1 = D0#data{ssh_params =
1508			    Ssh1#ssh{recv_sequence = ssh_transport:next_seqnum(Ssh1#ssh.recv_sequence)},
1509			decrypted_data_buffer = <<>>,
1510                        undecrypted_packet_length = undefined,
1511                        aead_data = <<>>,
1512			encrypted_data_buffer = EncryptedDataRest},
1513	    try
1514		ssh_message:decode(set_kex_overload_prefix(DecryptedBytes,D1))
1515	    of
1516		#ssh_msg_kexinit{} = Msg ->
1517		    {keep_state, D1, [{next_event, internal, prepare_next_packet},
1518				     {next_event, internal, {Msg,DecryptedBytes}}
1519				    ]};
1520
1521                #ssh_msg_global_request{}            = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1522                #ssh_msg_request_success{}           = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1523                #ssh_msg_request_failure{}           = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1524                #ssh_msg_channel_open{}              = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1525                #ssh_msg_channel_open_confirmation{} = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1526                #ssh_msg_channel_open_failure{}      = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1527                #ssh_msg_channel_window_adjust{}     = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1528                #ssh_msg_channel_data{}              = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1529                #ssh_msg_channel_extended_data{}     = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1530                #ssh_msg_channel_eof{}               = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1531                #ssh_msg_channel_close{}             = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1532                #ssh_msg_channel_request{}           = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1533                #ssh_msg_channel_failure{}           = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1534                #ssh_msg_channel_success{}           = Msg -> {keep_state, D1, ?CONNECTION_MSG(Msg)};
1535
1536		Msg ->
1537		    {keep_state, D1, [{next_event, internal, prepare_next_packet},
1538                                      {next_event, internal, Msg}
1539				    ]}
1540	    catch
1541		C:E:ST  ->
1542                    {Shutdown, D} =
1543                        ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
1544                                         io_lib:format("Bad packet: Decrypted, but can't decode~n~p:~p~n~p",
1545                                                       [C,E,ST]),
1546                                         StateName, D1),
1547                    {stop, Shutdown, D}
1548	    end;
1549
1550	{get_more, DecryptedBytes, EncryptedDataRest, AeadData, RemainingSshPacketLen, Ssh1} ->
1551	    %% Here we know that there are not enough bytes in
1552	    %% EncryptedDataRest to use. We must wait for more.
1553	    inet:setopts(Sock, [{active, once}]),
1554	    {keep_state, D0#data{encrypted_data_buffer = EncryptedDataRest,
1555				 decrypted_data_buffer = DecryptedBytes,
1556                                 undecrypted_packet_length = RemainingSshPacketLen,
1557                                 aead_data = AeadData,
1558				 ssh_params = Ssh1}};
1559
1560	{bad_mac, Ssh1} ->
1561            {Shutdown, D} =
1562                ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
1563                                 "Bad packet: bad mac",
1564                                 StateName, D0#data{ssh_params=Ssh1}),
1565            {stop, Shutdown, D};
1566
1567	{error, {exceeds_max_size,PacketLen}} ->
1568            {Shutdown, D} =
1569                ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
1570                                 io_lib:format("Bad packet: Size (~p bytes) exceeds max size",
1571                                               [PacketLen]),
1572                                 StateName, D0),
1573            {stop, Shutdown, D}
1574    catch
1575	C:E:ST ->
1576            {Shutdown, D} =
1577                ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR,
1578                                 io_lib:format("Bad packet: Couldn't decrypt~n~p:~p~n~p",[C,E,ST]),
1579                                 StateName, D0),
1580            {stop, Shutdown, D}
1581    end;
1582
1583
1584%%%====
1585handle_event(internal, prepare_next_packet, _, D) ->
1586    Enough =  erlang:max(8, D#data.ssh_params#ssh.decrypt_block_size),
1587    case size(D#data.encrypted_data_buffer) of
1588	Sz when Sz >= Enough ->
1589	    self() ! {D#data.transport_protocol, D#data.socket, <<>>};
1590	_ ->
1591	    ok
1592    end,
1593    inet:setopts(D#data.socket, [{active, once}]),
1594    keep_state_and_data;
1595
1596handle_event(info, {CloseTag,Socket}, _StateName,
1597	     D0 = #data{socket = Socket,
1598                        transport_close_tag = CloseTag,
1599                        connection_state = C0}) ->
1600    {Repls, D} = send_replies(ssh_connection:handle_stop(C0), D0),
1601    disconnect_fun("Received a transport close", D),
1602    {stop_and_reply, {shutdown,"Connection closed"}, Repls, D};
1603
1604handle_event(info, {timeout, {_, From} = Request}, _,
1605	     #data{connection_state = #connection{requests = Requests} = C0} = D) ->
1606    case lists:member(Request, Requests) of
1607	true ->
1608	    %% A channel request is not answered in time. Answer {error,timeout}
1609	    %% to the caller
1610	    C = C0#connection{requests = lists:delete(Request, Requests)},
1611	    {keep_state, D#data{connection_state=C}, [{reply,From,{error,timeout}}]};
1612	false ->
1613	    %% The request is answered - just ignore the timeout
1614	    keep_state_and_data
1615    end;
1616
1617%%% Handle that ssh channels user process goes down
1618handle_event(info, {'DOWN', _Ref, process, ChannelPid, _Reason}, _, D) ->
1619    Cache = cache(D),
1620    ssh_client_channel:cache_foldl(
1621      fun(#channel{user=U,
1622                   local_id=Id}, Acc) when U == ChannelPid ->
1623              ssh_client_channel:cache_delete(Cache, Id),
1624              Acc;
1625         (_,Acc) ->
1626              Acc
1627      end, [], Cache),
1628    {keep_state, D, cond_set_idle_timer(D)};
1629
1630handle_event({timeout,idle_time}, _Data,  _StateName, D) ->
1631    case ssh_client_channel:cache_info(num_entries, cache(D)) of
1632        0 ->
1633            {stop, {shutdown, "Timeout"}};
1634        _ ->
1635            keep_state_and_data
1636    end;
1637
1638%%% So that terminate will be run when supervisor is shutdown
1639handle_event(info, {'EXIT', _Sup, Reason}, StateName, _) ->
1640    Role = role(StateName),
1641    if
1642	Role == client ->
1643	    %% OTP-8111 tells this function clause fixes a problem in
1644	    %% clients, but there were no check for that role.
1645	    {stop, {shutdown, Reason}};
1646
1647	Reason == normal ->
1648	    %% An exit normal should not cause a server to crash. This has happend...
1649	    keep_state_and_data;
1650
1651	true ->
1652	    {stop, {shutdown, Reason}}
1653    end;
1654
1655handle_event(info, check_cache, _, D) ->
1656    {keep_state, D, cond_set_idle_timer(D)};
1657
1658handle_event(info, {fwd_connect_received, Sock, ChId, ChanCB}, StateName, #data{connection_state = Connection}) ->
1659    #connection{options = Options,
1660                channel_cache = Cache,
1661                sub_system_supervisor = SubSysSup} = Connection,
1662    Channel = ssh_client_channel:cache_lookup(Cache, ChId),
1663    {ok,Pid} = ssh_subsystem_sup:start_channel(role(StateName), SubSysSup, self(), ChanCB, ChId, [Sock], undefined, Options),
1664    ssh_client_channel:cache_update(Cache, Channel#channel{user=Pid}),
1665    gen_tcp:controlling_process(Sock, Pid),
1666    inet:setopts(Sock, [{active,once}]),
1667    keep_state_and_data;
1668
1669handle_event({call,From},
1670             {handle_direct_tcpip, ListenHost, ListenPort, ConnectToHost, ConnectToPort, _Timeout},
1671             _StateName,
1672             #data{connection_state = #connection{sub_system_supervisor=SubSysSup}}) ->
1673    case ssh_tcpip_forward_acceptor:supervised_start(ssh_subsystem_sup:tcpip_fwd_supervisor(SubSysSup),
1674                                                     {ListenHost, ListenPort},
1675                                                     {ConnectToHost, ConnectToPort},
1676                                                     "direct-tcpip", ssh_tcpip_forward_client,
1677                                                     self()) of
1678        {ok,LPort} ->
1679            {keep_state_and_data, [{reply,From,{ok,LPort}}]};
1680        {error,Error} ->
1681            {keep_state_and_data, [{reply,From,{error,Error}}]}
1682    end;
1683
1684handle_event(info, UnexpectedMessage, StateName, D = #data{ssh_params = Ssh}) ->
1685    case unexpected_fun(UnexpectedMessage, D) of
1686	report ->
1687	    Msg = lists:flatten(
1688		    io_lib:format(
1689                      "*** SSH: "
1690		      "Unexpected message '~p' received in state '~p'\n"
1691		      "Role: ~p\n"
1692		      "Peer: ~p\n"
1693		      "Local Address: ~p\n",
1694                      [UnexpectedMessage,
1695                       StateName,
1696                       Ssh#ssh.role,
1697                       Ssh#ssh.peer,
1698                       ?GET_INTERNAL_OPT(address, Ssh#ssh.opts, undefined)])),
1699	    error_logger:info_report(Msg),
1700	    keep_state_and_data;
1701
1702	skip ->
1703	    keep_state_and_data;
1704
1705	Other ->
1706	    Msg = lists:flatten(
1707		    io_lib:format("*** SSH: "
1708                                  "Call to fun in 'unexpectedfun' failed:~n"
1709				  "Return: ~p\n"
1710				  "Message: ~p\n"
1711				  "Role: ~p\n"
1712				  "Peer: ~p\n"
1713				  "Local Address: ~p\n",
1714                                  [Other,
1715                                   UnexpectedMessage,
1716                                   Ssh#ssh.role,
1717                                   Ssh#ssh.peer,
1718                                   ?GET_INTERNAL_OPT(address, Ssh#ssh.opts, undefined)]
1719				 )),
1720	    error_logger:error_report(Msg),
1721	    keep_state_and_data
1722    end;
1723
1724handle_event(internal, {send_disconnect,Code,DetailedText,Module,Line}, StateName, D0) ->
1725    {Shutdown, D} =
1726        send_disconnect(Code, DetailedText, Module, Line, StateName, D0),
1727    {stop, Shutdown, D};
1728
1729
1730handle_event(enter, _OldState, State, D) ->
1731    %% Just skip
1732    {next_state, State, D};
1733
1734handle_event(_Type, _Msg, {ext_info,Role,_ReNegFlag}, D) ->
1735    %% If something else arrives, goto next state and handle the event in that one
1736    {next_state, {connected,Role}, D, [postpone]};
1737
1738handle_event(Type, Ev, StateName, D0) ->
1739    Details =
1740	case catch atom_to_list(element(1,Ev)) of
1741	    "ssh_msg_" ++_ when Type==internal ->
1742                lists:flatten(io_lib:format("Message ~p in wrong state (~p)", [element(1,Ev), StateName]));
1743	    _ ->
1744		io_lib:format("Unhandled event in state ~p:~n~p", [StateName,Ev])
1745	end,
1746    {Shutdown, D} =
1747        ?send_disconnect(?SSH_DISCONNECT_PROTOCOL_ERROR, Details, StateName, D0),
1748    {stop, Shutdown, D}.
1749
1750
1751%%--------------------------------------------------------------------
1752-spec terminate(any(),
1753		state_name(),
1754		#data{}
1755	       ) -> term().
1756
1757%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1758
1759terminate(normal, _StateName, D) ->
1760    stop_subsystem(D),
1761    close_transport(D);
1762
1763terminate({shutdown,"Connection closed"}, _StateName, D) ->
1764    %% Normal: terminated by a sent by peer
1765    stop_subsystem(D),
1766    close_transport(D);
1767
1768terminate({shutdown,{init,Reason}}, StateName, D) ->
1769    %% Error in initiation. "This error should not occur".
1770    log(error, D, "Shutdown in init (StateName=~p): ~p~n", [StateName,Reason]),
1771    stop_subsystem(D),
1772    close_transport(D);
1773
1774terminate({shutdown,_R}, _StateName, D) ->
1775    %% Internal termination, usually already reported via ?send_disconnect resulting in a log entry
1776    stop_subsystem(D),
1777    close_transport(D);
1778
1779terminate(shutdown, _StateName, D0) ->
1780    %% Terminated by supervisor
1781    %% Use send_msg directly instead of ?send_disconnect to avoid filling the log
1782    D = send_msg(#ssh_msg_disconnect{code = ?SSH_DISCONNECT_BY_APPLICATION,
1783                                     description = "Terminated (shutdown) by supervisor"},
1784                 D0),
1785    close_transport(D);
1786
1787terminate(killed, _StateName, D) ->
1788    %% Got a killed signal
1789    stop_subsystem(D),
1790    close_transport(D);
1791
1792terminate(Reason, StateName, D0) ->
1793    %% Others, e.g  undef, {badmatch,_}, ...
1794    log(error, D0, Reason),
1795    {_ShutdownReason, D} = ?send_disconnect(?SSH_DISCONNECT_BY_APPLICATION,
1796                                            "Internal error",
1797                                            io_lib:format("Reason: ~p",[Reason]),
1798                                            StateName, D0),
1799    stop_subsystem(D),
1800    close_transport(D).
1801
1802%%--------------------------------------------------------------------
1803
1804%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1805
1806format_status(normal, [_, _StateName, D]) ->
1807    [{data, [{"State", D}]}];
1808format_status(terminate, [_, _StateName, D]) ->
1809    [{data, [{"State", clean(D)}]}].
1810
1811
1812clean(#data{}=R) ->
1813    fmt_stat_rec(record_info(fields,data), R,
1814                 [decrypted_data_buffer,
1815                  encrypted_data_buffer,
1816                  key_exchange_init_msg,
1817                  user_passwords,
1818                  opts,
1819                  inet_initial_recbuf_size]);
1820clean(#ssh{}=R) ->
1821    fmt_stat_rec(record_info(fields, ssh), R,
1822                 [c_keyinit,
1823                  s_keyinit,
1824                  send_mac_key,
1825                  send_mac_size,
1826                  recv_mac_key,
1827                  recv_mac_size,
1828                  encrypt_keys,
1829                  encrypt_ctx,
1830                  decrypt_keys,
1831                  decrypt_ctx,
1832                  compress_ctx,
1833                  decompress_ctx,
1834                  shared_secret,
1835                  exchanged_hash,
1836                  session_id,
1837                  keyex_key,
1838                  keyex_info,
1839                  available_host_keys]);
1840clean(#connection{}=R) ->
1841    fmt_stat_rec(record_info(fields, connection), R,
1842                 []);
1843clean(L) when is_list(L) ->
1844    lists:map(fun clean/1, L);
1845clean(T) when is_tuple(T) ->
1846    list_to_tuple( clean(tuple_to_list(T)));
1847clean(X) ->
1848    ssh_options:no_sensitive(filter, X).
1849
1850
1851fmt_stat_rec(FieldNames, Rec, Exclude) ->
1852    Values = tl(tuple_to_list(Rec)),
1853    list_to_tuple(
1854      [element(1,Rec) |
1855       lists:map(fun({K,V}) ->
1856                         case lists:member(K, Exclude) of
1857                             true -> '****';
1858                             false -> clean(V)
1859                         end
1860                 end, lists:zip(FieldNames, Values))
1861      ]).
1862
1863%%--------------------------------------------------------------------
1864-spec code_change(term() | {down,term()},
1865		  state_name(),
1866		  #data{},
1867		  term()
1868		 ) -> {ok, state_name(), #data{}}.
1869
1870%% . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
1871
1872code_change(_OldVsn, StateName, State, _Extra) ->
1873    {ok, StateName, State}.
1874
1875
1876%%====================================================================
1877%% Internal functions
1878%%====================================================================
1879
1880%%--------------------------------------------------------------------
1881%% Starting
1882
1883start_the_connection_child(UserPid, Role, Socket, Options0) ->
1884    Sups = ?GET_INTERNAL_OPT(supervisors, Options0),
1885    ConnectionSup = proplists:get_value(connection_sup, Sups),
1886    Options = ?PUT_INTERNAL_OPT({user_pid,UserPid}, Options0),
1887    InitArgs = [Role, Socket, Options],
1888    {ok, Pid} = ssh_connection_sup:start_child(ConnectionSup, InitArgs),
1889    ok = socket_control(Socket, Pid, Options), % transfer the Socket ownership in a controlled way.
1890    Pid.
1891
1892%%--------------------------------------------------------------------
1893%% Stopping
1894
1895stop_subsystem(#data{ssh_params =
1896                         #ssh{role = Role},
1897                     connection_state =
1898                         #connection{system_supervisor = SysSup,
1899                                     sub_system_supervisor = SubSysSup,
1900                                     options = Opts}
1901                    }) when is_pid(SysSup) andalso is_pid(SubSysSup)  ->
1902    C = self(),
1903    spawn(fun() ->
1904                  wait_until_dead(C, 10000),
1905                  case {Role, ?GET_INTERNAL_OPT(connected_socket,Opts,non_socket_started)} of
1906                      {server, non_socket_started} ->
1907                          ssh_system_sup:stop_subsystem(SysSup, SubSysSup);
1908                      {client, non_socket_started} ->
1909                          ssh_system_sup:stop_system(Role, SysSup);
1910                      {server, _Socket} ->
1911                          ssh_system_sup:stop_system(Role, SysSup);
1912                      {client, _Socket} ->
1913                          ssh_system_sup:stop_subsystem(SysSup, SubSysSup),
1914                          wait_until_dead(SubSysSup, 1000),
1915                          sshc_sup:stop_system(SysSup)
1916                  end
1917          end);
1918stop_subsystem(_) ->
1919    ok.
1920
1921
1922wait_until_dead(Pid, Timeout) ->
1923    Mref = erlang:monitor(process, Pid),
1924    receive
1925        {'DOWN', Mref, process, Pid, _Info} -> ok
1926    after
1927        Timeout -> ok
1928    end.
1929
1930
1931close_transport(#data{transport_cb = Transport,
1932                      socket = Socket}) ->
1933    try
1934        Transport:close(Socket)
1935    of
1936        _ -> ok
1937    catch
1938        _:_ -> ok
1939    end.
1940
1941%%--------------------------------------------------------------------
1942%% "Invert" the Role
1943peer_role(client) -> server;
1944peer_role(server) -> client.
1945
1946%%--------------------------------------------------------------------
1947available_hkey_algorithms(client, Options) ->
1948    case available_hkey_algos(Options) of
1949        [] ->
1950            error({shutdown, "No public key algs"});
1951        Algs ->
1952	    [atom_to_list(A) || A<-Algs]
1953    end;
1954
1955available_hkey_algorithms(server, Options) ->
1956    case [A || A <- available_hkey_algos(Options),
1957               is_usable_host_key(A, Options)] of
1958        [] ->
1959            error({shutdown, "No host key available"});
1960	Algs ->
1961	    [atom_to_list(A) || A<-Algs]
1962    end.
1963
1964
1965available_hkey_algos(Options) ->
1966    SupAlgos = ssh_transport:supported_algorithms(public_key),
1967    HKeys = proplists:get_value(public_key,
1968                                ?GET_OPT(preferred_algorithms,Options)
1969                               ),
1970    NonSupported =  HKeys -- SupAlgos,
1971    AvailableAndSupported = HKeys -- NonSupported,
1972    AvailableAndSupported.
1973
1974
1975send_msg(Msg, State=#data{ssh_params=Ssh0}) when is_tuple(Msg) ->
1976    {Bytes, Ssh} = ssh_transport:ssh_packet(Msg, Ssh0),
1977    send_bytes(Bytes, State),
1978    State#data{ssh_params=Ssh}.
1979
1980send_bytes("", _D) ->
1981    ok;
1982send_bytes(Bytes, #data{socket = Socket, transport_cb = Transport}) ->
1983    _ = Transport:send(Socket, Bytes),
1984    ok.
1985
1986handle_version({2, 0} = NumVsn, StrVsn, Ssh0) ->
1987    Ssh = counterpart_versions(NumVsn, StrVsn, Ssh0),
1988    {ok, Ssh};
1989handle_version(_,_,_) ->
1990    not_supported.
1991
1992string_version(#ssh{role = client, c_version = Vsn}) ->
1993    Vsn;
1994string_version(#ssh{role = server, s_version = Vsn}) ->
1995    Vsn.
1996
1997
1998cast(FsmPid, Event) ->
1999    gen_statem:cast(FsmPid, Event).
2000
2001call(FsmPid, Event) ->
2002    call(FsmPid, Event, infinity).
2003
2004call(FsmPid, Event, Timeout) ->
2005    try gen_statem:call(FsmPid, Event, Timeout) of
2006	{closed, _R} ->
2007	    {error, closed};
2008	{killed, _R} ->
2009	    {error, closed};
2010	Result ->
2011	    Result
2012    catch
2013	exit:{noproc, _R} ->
2014	    {error, closed};
2015	exit:{normal, _R} ->
2016	    {error, closed};
2017	exit:{{shutdown, _R},_} ->
2018	    {error, closed};
2019	exit:{shutdown, _R} ->
2020	    {error, closed}
2021    end.
2022
2023
2024set_kex_overload_prefix(Msg = <<?BYTE(Op),_/binary>>, #data{ssh_params=SshParams})
2025  when Op == 30;
2026       Op == 31
2027       ->
2028    case catch atom_to_list(kex(SshParams)) of
2029	"ecdh-sha2-" ++ _ ->
2030	    <<"ecdh",Msg/binary>>;
2031        "curve25519-" ++ _ ->
2032	    <<"ecdh",Msg/binary>>;
2033        "curve448-" ++ _ ->
2034	    <<"ecdh",Msg/binary>>;
2035	"diffie-hellman-group-exchange-" ++ _ ->
2036	    <<"dh_gex",Msg/binary>>;
2037	"diffie-hellman-group" ++ _ ->
2038	    <<"dh",Msg/binary>>;
2039	_ ->
2040	    Msg
2041    end;
2042set_kex_overload_prefix(Msg, _) ->
2043    Msg.
2044
2045kex(#ssh{algorithms=#alg{kex=Kex}}) -> Kex;
2046kex(_) -> undefined.
2047
2048cache(#data{connection_state=C}) -> C#connection.channel_cache.
2049
2050
2051%%%----------------------------------------------------------------
2052handle_ssh_msg_ext_info(#ssh_msg_ext_info{}, D=#data{ssh_params = #ssh{recv_ext_info=false}} ) ->
2053    % The peer sent this although we didn't allow it!
2054    D;
2055
2056handle_ssh_msg_ext_info(#ssh_msg_ext_info{data=Data}, D0) ->
2057    lists:foldl(fun ext_info/2, D0, Data).
2058
2059
2060ext_info({"server-sig-algs",SigAlgsStr},
2061         D0 = #data{ssh_params=#ssh{role=client,
2062                                    userauth_pubkeys=ClientSigAlgs}=Ssh0}) ->
2063    %% ClientSigAlgs are the pub_key algortithms that:
2064    %%  1) is usable, that is, the user has such a public key and
2065    %%  2) is either the default list or set by the caller
2066    %%     with the client option 'pref_public_key_algs'
2067    %%
2068    %% The list is already checked for duplicates.
2069
2070    SigAlgs = [A || Astr <- string:tokens(SigAlgsStr, ","),
2071                    A <- try [list_to_existing_atom(Astr)]
2072                              %% list_to_existing_atom will fail for unknown algorithms
2073                         catch _:_ -> []
2074                         end],
2075
2076    CommonAlgs = [A || A <- SigAlgs,
2077                       lists:member(A, ClientSigAlgs)],
2078
2079    %% Re-arrange the client supported public-key algorithms so that the server
2080    %% preferred ones are tried first.
2081    %% Trying algorithms not mentioned by the server is ok, since the server can't know
2082    %% if the client supports 'server-sig-algs' or not.
2083
2084    D0#data{
2085      ssh_params =
2086          Ssh0#ssh{
2087            userauth_pubkeys =
2088                CommonAlgs ++ (ClientSigAlgs -- CommonAlgs)
2089           }};
2090
2091ext_info(_, D0) ->
2092    %% Not implemented
2093    D0.
2094
2095%%%----------------------------------------------------------------
2096is_usable_user_pubkey(Alg, Ssh) ->
2097    try ssh_auth:get_public_key(Alg, Ssh) of
2098        {ok,_} -> true;
2099        _ -> false
2100    catch
2101        _:_ -> false
2102    end.
2103
2104%%%----------------------------------------------------------------
2105is_usable_host_key(Alg, Opts) ->
2106    try ssh_transport:get_host_key(Alg, Opts)
2107    of
2108        _PrivHostKey -> true
2109    catch
2110        _:_ -> false
2111    end.
2112
2113%%%----------------------------------------------------------------
2114handle_request(ChannelPid, ChannelId, Type, Data, WantReply, From, D) ->
2115    case ssh_client_channel:cache_lookup(cache(D), ChannelId) of
2116	#channel{remote_id = Id,
2117                 sent_close = false} = Channel ->
2118	    update_sys(cache(D), Channel, Type, ChannelPid),
2119	    send_msg(ssh_connection:channel_request_msg(Id, Type, WantReply, Data),
2120		     add_request(WantReply, ChannelId, From, D));
2121
2122        _ when WantReply==true ->
2123            {error,closed};
2124
2125        _ ->
2126            D
2127    end.
2128
2129handle_request(ChannelId, Type, Data, WantReply, From, D) ->
2130    case ssh_client_channel:cache_lookup(cache(D), ChannelId) of
2131	#channel{remote_id = Id,
2132                 sent_close = false} ->
2133	    send_msg(ssh_connection:channel_request_msg(Id, Type, WantReply, Data),
2134		     add_request(WantReply, ChannelId, From, D));
2135
2136	_ when WantReply==true ->
2137            {error,closed};
2138
2139        _ ->
2140            D
2141    end.
2142
2143%%%----------------------------------------------------------------
2144update_sys(Cache, Channel, Type, ChannelPid) ->
2145    ssh_client_channel:cache_update(Cache,
2146			     Channel#channel{sys = Type, user = ChannelPid}).
2147
2148add_request(false, _ChannelId, _From, State) ->
2149    State;
2150add_request(true, ChannelId, From, #data{connection_state =
2151					     #connection{requests = Requests0} =
2152					     Connection} = State) ->
2153    Requests = [{ChannelId, From} | Requests0],
2154    State#data{connection_state = Connection#connection{requests = Requests}};
2155add_request(Fun, ChannelId, From, #data{connection_state =
2156                                            #connection{requests = Requests0} =
2157                                            Connection} = State) when is_function(Fun) ->
2158    Requests = [{ChannelId, From, Fun} | Requests0],
2159    State#data{connection_state = Connection#connection{requests = Requests}}.
2160
2161new_channel_id(#data{connection_state = #connection{channel_id_seed = Id} =
2162			 Connection}
2163	       = State) ->
2164    {Id, State#data{connection_state =
2165			Connection#connection{channel_id_seed = Id + 1}}}.
2166
2167
2168%%%----------------------------------------------------------------
2169start_rekeying(Role, D0) ->
2170    {KeyInitMsg, SshPacket, Ssh} = ssh_transport:key_exchange_init_msg(D0#data.ssh_params),
2171    send_bytes(SshPacket, D0),
2172    D = D0#data{ssh_params = Ssh,
2173                key_exchange_init_msg = KeyInitMsg},
2174    {next_state, {kexinit,Role,renegotiate}, D}.
2175
2176
2177init_renegotiate_timers(_OldState, NewState, D) ->
2178    {RekeyTimeout,_MaxSent} = ?GET_OPT(rekey_limit, (D#data.ssh_params)#ssh.opts),
2179    {next_state, NewState, D, [{{timeout,renegotiate},     RekeyTimeout,       none},
2180                               {{timeout,check_data_size}, ?REKEY_DATA_TIMOUT, none} ]}.
2181
2182
2183pause_renegotiate_timers(_OldState, NewState, D) ->
2184    {next_state, NewState, D, [{{timeout,renegotiate},     infinity, none},
2185                               {{timeout,check_data_size}, infinity, none} ]}.
2186
2187check_data_rekeying(Role, D) ->
2188    case inet:getstat(D#data.socket, [send_oct]) of
2189        {ok, [{send_oct,SocketSentTotal}]} ->
2190            SentSinceRekey = SocketSentTotal - D#data.last_size_rekey,
2191            {_RekeyTimeout,MaxSent} = ?GET_OPT(rekey_limit, (D#data.ssh_params)#ssh.opts),
2192            case check_data_rekeying_dbg(SentSinceRekey, MaxSent) of
2193                true ->
2194                    start_rekeying(Role, D#data{last_size_rekey = SocketSentTotal});
2195                _ ->
2196                    %% Not enough data sent for a re-negotiation. Restart timer.
2197                    {keep_state, D, {{timeout,check_data_size}, ?REKEY_DATA_TIMOUT, none}}
2198            end;
2199        {error,_} ->
2200            %% Socket closed, but before this module has handled that. Maybe
2201            %% it is in the message queue.
2202            %% Just go on like if there was not enough data transmitted to start re-keying:
2203            {keep_state, D, {{timeout,check_data_size}, ?REKEY_DATA_TIMOUT, none}}
2204    end.
2205
2206check_data_rekeying_dbg(SentSinceRekey, MaxSent) ->
2207    %% This function is for the ssh_dbg to trace on. See dbg_trace/3 at the end.
2208    SentSinceRekey >= MaxSent.
2209
2210%%%----------------------------------------------------------------
2211%%% This server/client has decided to disconnect via the state machine:
2212%%% The unused arguments are for debugging.
2213
2214send_disconnect(Code, DetailedText, Module, Line, StateName, D) ->
2215    send_disconnect(Code, default_text(Code), DetailedText, Module, Line, StateName, D).
2216
2217send_disconnect(Code, Reason, DetailedText, Module, Line, StateName, D0) ->
2218    Msg = #ssh_msg_disconnect{code = Code,
2219                              description = Reason},
2220    D = send_msg(Msg, D0),
2221    LogMsg = io_lib:format("Disconnects with code = ~p [RFC4253 11.1]: ~s",[Code,Reason]),
2222    call_disconnectfun_and_log_cond(LogMsg, DetailedText, Module, Line, StateName, D),
2223    {{shutdown,Reason}, D}.
2224
2225call_disconnectfun_and_log_cond(LogMsg, DetailedText, Module, Line, StateName, D) ->
2226    case disconnect_fun(LogMsg, D) of
2227        void ->
2228            log(info, D,
2229                "~s~n"
2230                "State = ~p~n"
2231                "Module = ~p, Line = ~p.~n"
2232                "Details:~n  ~s~n",
2233                [LogMsg, StateName, Module, Line, DetailedText]);
2234        _ ->
2235            ok
2236    end.
2237
2238
2239default_text(?SSH_DISCONNECT_HOST_NOT_ALLOWED_TO_CONNECT) -> "Host not allowed to connect";
2240default_text(?SSH_DISCONNECT_PROTOCOL_ERROR) -> "Protocol error";
2241default_text(?SSH_DISCONNECT_KEY_EXCHANGE_FAILED) -> "Key exchange failed";
2242default_text(?SSH_DISCONNECT_RESERVED) -> "Reserved";
2243default_text(?SSH_DISCONNECT_MAC_ERROR) -> "Mac error";
2244default_text(?SSH_DISCONNECT_COMPRESSION_ERROR) -> "Compression error";
2245default_text(?SSH_DISCONNECT_SERVICE_NOT_AVAILABLE) -> "Service not available";
2246default_text(?SSH_DISCONNECT_PROTOCOL_VERSION_NOT_SUPPORTED) -> "Protocol version not supported";
2247default_text(?SSH_DISCONNECT_HOST_KEY_NOT_VERIFIABLE) -> "Host key not verifiable";
2248default_text(?SSH_DISCONNECT_CONNECTION_LOST) -> "Connection lost";
2249default_text(?SSH_DISCONNECT_BY_APPLICATION) -> "By application";
2250default_text(?SSH_DISCONNECT_TOO_MANY_CONNECTIONS) -> "Too many connections";
2251default_text(?SSH_DISCONNECT_AUTH_CANCELLED_BY_USER) -> "Auth cancelled by user";
2252default_text(?SSH_DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE) -> "Unable to connect using the available authentication methods";
2253default_text(?SSH_DISCONNECT_ILLEGAL_USER_NAME) -> "Illegal user name".
2254
2255%%%----------------------------------------------------------------
2256counterpart_versions(NumVsn, StrVsn, #ssh{role = server} = Ssh) ->
2257    Ssh#ssh{c_vsn = NumVsn , c_version = StrVsn};
2258counterpart_versions(NumVsn, StrVsn, #ssh{role = client} = Ssh) ->
2259    Ssh#ssh{s_vsn = NumVsn , s_version = StrVsn}.
2260
2261%%%----------------------------------------------------------------
2262conn_info_keys() ->
2263    [client_version,
2264     server_version,
2265     peer,
2266     user,
2267     sockname,
2268     options,
2269     algorithms,
2270     channels
2271    ].
2272
2273conn_info(client_version, #data{ssh_params=S}) -> {S#ssh.c_vsn, S#ssh.c_version};
2274conn_info(server_version, #data{ssh_params=S}) -> {S#ssh.s_vsn, S#ssh.s_version};
2275conn_info(peer,           #data{ssh_params=S}) -> S#ssh.peer;
2276conn_info(user,                             D) -> D#data.auth_user;
2277conn_info(sockname,       #data{ssh_params=S}) -> S#ssh.local;
2278conn_info(options,        #data{ssh_params=#ssh{opts=Opts}})    -> lists:sort(
2279                                                                     maps:to_list(
2280                                                                       ssh_options:keep_set_options(
2281                                                                         client,
2282                                                                         ssh_options:keep_user_options(client,Opts))));
2283conn_info(algorithms,     #data{ssh_params=#ssh{algorithms=A}}) -> conn_info_alg(A);
2284conn_info(channels, D) -> try conn_info_chans(ets:tab2list(cache(D)))
2285                          catch _:_ -> undefined
2286                          end;
2287%% dbg options ( = not documented):
2288conn_info(socket, D) ->   D#data.socket;
2289conn_info(chan_ids, D) ->
2290    ssh_client_channel:cache_foldl(fun(#channel{local_id=Id}, Acc) ->
2291				    [Id | Acc]
2292			    end, [], cache(D)).
2293
2294conn_info_chans(Chs) ->
2295    Fs = record_info(fields, channel),
2296    [lists:zip(Fs, tl(tuple_to_list(Ch))) || Ch=#channel{} <- Chs].
2297
2298conn_info_alg(AlgTup) ->
2299    [alg|Vs] = tuple_to_list(AlgTup),
2300    Fs = record_info(fields, alg),
2301    [{K,V} || {K,V} <- lists:zip(Fs,Vs),
2302              lists:member(K,[kex,
2303                              hkey,
2304                              encrypt,
2305                              decrypt,
2306                              send_mac,
2307                              recv_mac,
2308                              compress,
2309                              decompress,
2310                              send_ext_info,
2311                              recv_ext_info])].
2312
2313%%%----------------------------------------------------------------
2314chann_info(recv_window, C) ->
2315    {{win_size,    C#channel.recv_window_size},
2316     {packet_size, C#channel.recv_packet_size}};
2317chann_info(send_window, C) ->
2318    {{win_size,    C#channel.send_window_size},
2319     {packet_size, C#channel.send_packet_size}};
2320%% dbg options ( = not documented):
2321chann_info(pid, C) ->
2322    C#channel.user.
2323
2324%%%----------------------------------------------------------------
2325%% Assisting meta function for the *_info functions
2326fold_keys(Keys, Fun, Extra) ->
2327    lists:foldr(fun(Key, Acc) ->
2328			try Fun(Key, Extra) of
2329			    Value -> [{Key,Value}|Acc]
2330			catch
2331			    _:_ -> Acc
2332			end
2333		end, [], Keys).
2334
2335%%%----------------------------------------------------------------
2336log(Tag, D, Format, Args) ->
2337    log(Tag, D, io_lib:format(Format,Args)).
2338
2339log(Tag, D, Reason) ->
2340    case atom_to_list(Tag) of                   % Dialyzer-technical reasons...
2341        "error"   -> do_log(error_msg,   Reason, D);
2342        "warning" -> do_log(warning_msg, Reason, D);
2343        "info"    -> do_log(info_msg,    Reason, D)
2344    end.
2345
2346
2347do_log(F, Reason0, #data{ssh_params = S}) ->
2348    Reason =
2349        try io_lib:format("~s",[Reason0])
2350        of _ -> Reason0
2351        catch
2352            _:_ -> io_lib:format("~p",[Reason0])
2353        end,
2354    case S of
2355        #ssh{role = Role} when Role==server ;
2356                               Role==client ->
2357            {PeerRole,PeerVersion} =
2358                case Role of
2359                    server -> {"Client", S#ssh.c_version};
2360                    client -> {"Server", S#ssh.s_version}
2361                end,
2362            error_logger:F("Erlang SSH ~p ~s ~s.~n"
2363                           "~s: ~p~n"
2364                           "~s~n",
2365                           [Role,
2366                            ssh_log_version(), crypto_log_info(),
2367                            PeerRole, PeerVersion,
2368                            Reason]);
2369        _ ->
2370            error_logger:F("Erlang SSH ~s ~s.~n"
2371                           "~s~n",
2372                           [ssh_log_version(), crypto_log_info(),
2373                            Reason])
2374    end.
2375
2376crypto_log_info() ->
2377    try
2378        [{_,_,CI}] = crypto:info_lib(),
2379        case crypto:info_fips() of
2380            enabled ->
2381                <<"(",CI/binary,". FIPS enabled)">>;
2382            not_enabled ->
2383                <<"(",CI/binary,". FIPS available but not enabled)">>;
2384            _ ->
2385                <<"(",CI/binary,")">>
2386        end
2387    catch
2388        _:_ -> ""
2389    end.
2390
2391ssh_log_version() ->
2392    case application:get_key(ssh,vsn) of
2393        {ok,Vsn} -> Vsn;
2394        undefined -> ""
2395    end.
2396
2397%%%----------------------------------------------------------------
2398not_connected_filter({connection_reply, _Data}) -> true;
2399not_connected_filter(_) -> false.
2400
2401%%%----------------------------------------------------------------
2402
2403send_replies({Repls,C = #connection{}}, D) when is_list(Repls) ->
2404    send_replies(Repls, D#data{connection_state=C});
2405send_replies(Repls, State) ->
2406    lists:foldl(fun get_repl/2, {[],State}, Repls).
2407
2408get_repl({connection_reply,Msg}, {CallRepls,S}) ->
2409    if is_record(Msg, ssh_msg_channel_success) ->
2410	    update_inet_buffers(S#data.socket);
2411       true ->
2412	    ok
2413    end,
2414    {CallRepls, send_msg(Msg,S)};
2415get_repl({channel_data,undefined,_Data}, Acc) ->
2416    Acc;
2417get_repl({channel_data,Pid,Data}, Acc) ->
2418    Pid ! {ssh_cm, self(), Data},
2419    Acc;
2420get_repl({channel_request_reply,From,Data}, {CallRepls,S}) ->
2421    {[{reply,From,Data}|CallRepls], S};
2422get_repl({flow_control,Cache,Channel,From,Msg}, {CallRepls,S}) ->
2423    ssh_client_channel:cache_update(Cache, Channel#channel{flow_control = undefined}),
2424    {[{reply,From,Msg}|CallRepls], S};
2425get_repl({flow_control,From,Msg}, {CallRepls,S}) ->
2426    {[{reply,From,Msg}|CallRepls], S};
2427%% get_repl(noreply, Acc) ->
2428%%     Acc;
2429%% get_repl([], Acc) ->
2430%%     Acc;
2431get_repl(X, Acc) ->
2432    exit({get_repl,X,Acc}).
2433
2434%%%----------------------------------------------------------------
2435-define(CALL_FUN(Key,D), catch (?GET_OPT(Key, (D#data.ssh_params)#ssh.opts)) ).
2436
2437%%disconnect_fun({disconnect,Msg}, D) -> ?CALL_FUN(disconnectfun,D)(Msg);
2438disconnect_fun(Reason, D)           -> ?CALL_FUN(disconnectfun,D)(Reason).
2439
2440unexpected_fun(UnexpectedMessage, #data{ssh_params = #ssh{peer = {_,Peer} }} = D) ->
2441    ?CALL_FUN(unexpectedfun,D)(UnexpectedMessage, Peer).
2442
2443debug_fun(#ssh_msg_debug{always_display = Display,
2444			 message = DbgMsg,
2445			 language = Lang},
2446	  D) ->
2447    ?CALL_FUN(ssh_msg_debug_fun,D)(self(), Display, DbgMsg, Lang).
2448
2449
2450connected_fun(User, Method, #data{ssh_params = #ssh{peer = {_,Peer}}} = D) ->
2451    ?CALL_FUN(connectfun,D)(User, Peer, Method).
2452
2453
2454retry_fun(_, undefined, _) ->
2455    ok;
2456retry_fun(User, Reason, #data{ssh_params = #ssh{opts = Opts,
2457						peer = {_,Peer}
2458					       }}) ->
2459    {Tag,Info} =
2460	case Reason of
2461	    {error, Error} ->
2462		{failfun, Error};
2463	    _ ->
2464		{infofun, Reason}
2465	end,
2466    Fun = ?GET_OPT(Tag, Opts),
2467    try erlang:fun_info(Fun, arity)
2468    of
2469	{arity, 2} -> %% Backwards compatible
2470	    catch Fun(User, Info);
2471	{arity, 3} ->
2472	    catch Fun(User, Peer, Info);
2473	_ ->
2474	    ok
2475    catch
2476	_:_ ->
2477	    ok
2478    end.
2479
2480%%%----------------------------------------------------------------
2481%%% Cache idle timer that closes the connection if there are no
2482%%% channels open for a while.
2483
2484cond_set_idle_timer(D) ->
2485    case ssh_client_channel:cache_info(num_entries, cache(D)) of
2486        0 -> {{timeout,idle_time}, ?GET_OPT(idle_time, (D#data.ssh_params)#ssh.opts), none};
2487        _ -> {{timeout,idle_time}, infinity, none}
2488    end.
2489
2490%%%----------------------------------------------------------------
2491start_channel_request_timer(_,_, infinity) ->
2492    ok;
2493start_channel_request_timer(Channel, From, Time) ->
2494    erlang:send_after(Time, self(), {timeout, {Channel, From}}).
2495
2496%%%----------------------------------------------------------------
2497%%% Connection start and initalization helpers
2498
2499socket_control(Socket, Pid, Options) ->
2500    {_, Callback, _} =	?GET_OPT(transport, Options),
2501    case Callback:controlling_process(Socket, Pid) of
2502	ok ->
2503	    gen_statem:cast(Pid, socket_control);
2504	{error, Reason}	->
2505	    {error, Reason}
2506    end.
2507
2508handshake(Pid, Ref, Timeout) ->
2509    receive
2510	ssh_connected ->
2511	    erlang:demonitor(Ref),
2512	    {ok, Pid};
2513	{Pid, not_connected, Reason} ->
2514	    {error, Reason};
2515	{Pid, user_password} ->
2516	    Pass = io:get_password(),
2517	    Pid ! Pass,
2518	    handshake(Pid, Ref, Timeout);
2519	{Pid, question} ->
2520	    Answer = io:get_line(""),
2521	    Pid ! Answer,
2522	    handshake(Pid, Ref, Timeout);
2523	{'DOWN', _, process, Pid, {shutdown, Reason}} ->
2524	    {error, Reason};
2525	{'DOWN', _, process, Pid, Reason} ->
2526	    {error, Reason}
2527    after Timeout ->
2528	    stop(Pid),
2529	    {error, timeout}
2530    end.
2531
2532update_inet_buffers(Socket) ->
2533    try
2534        {ok, BufSzs0} = inet:getopts(Socket, [sndbuf,recbuf]),
2535        MinVal = 655360,
2536        [{Tag,MinVal} || {Tag,Val} <- BufSzs0,
2537                         Val < MinVal]
2538    of
2539	[] -> ok;
2540	NewOpts -> inet:setopts(Socket, NewOpts)
2541    catch
2542        _:_ -> ok
2543    end.
2544
2545%%%################################################################
2546%%%#
2547%%%# Tracing
2548%%%#
2549
2550ssh_dbg_trace_points() -> [terminate, disconnect, connections, connection_events, renegotiation].
2551
2552ssh_dbg_flags(connections) -> [c | ssh_dbg_flags(terminate)];
2553ssh_dbg_flags(renegotiation) -> [c];
2554ssh_dbg_flags(connection_events) -> [c];
2555ssh_dbg_flags(terminate) -> [c];
2556ssh_dbg_flags(disconnect) -> [c].
2557
2558ssh_dbg_on(connections) -> dbg:tp(?MODULE,  init_connection_handler, 3, x),
2559                           ssh_dbg_on(terminate);
2560ssh_dbg_on(connection_events) -> dbg:tp(?MODULE,   handle_event, 4, x);
2561ssh_dbg_on(renegotiation) -> dbg:tpl(?MODULE,   init_renegotiate_timers, 3, x),
2562                             dbg:tpl(?MODULE,   pause_renegotiate_timers, 3, x),
2563                             dbg:tpl(?MODULE,   check_data_rekeying_dbg, 2, x),
2564                             dbg:tpl(?MODULE,   start_rekeying, 2, x),
2565                             dbg:tp(?MODULE,   renegotiate, 1, x);
2566ssh_dbg_on(terminate) -> dbg:tp(?MODULE,  terminate, 3, x);
2567ssh_dbg_on(disconnect) -> dbg:tpl(?MODULE,  send_disconnect, 7, x).
2568
2569
2570ssh_dbg_off(disconnect) -> dbg:ctpl(?MODULE, send_disconnect, 7);
2571ssh_dbg_off(terminate) -> dbg:ctpg(?MODULE, terminate, 3);
2572ssh_dbg_off(renegotiation) -> dbg:ctpl(?MODULE,   init_renegotiate_timers, 3),
2573                              dbg:ctpl(?MODULE,   pause_renegotiate_timers, 3),
2574                              dbg:ctpl(?MODULE,   check_data_rekeying_dbg, 2),
2575                              dbg:ctpl(?MODULE,   start_rekeying, 2),
2576                              dbg:ctpg(?MODULE,   renegotiate, 1);
2577ssh_dbg_off(connection_events) -> dbg:ctpg(?MODULE, handle_event, 4);
2578ssh_dbg_off(connections) -> dbg:ctpg(?MODULE, init_connection_handler, 3),
2579                            ssh_dbg_off(terminate).
2580
2581
2582ssh_dbg_format(connections, {call, {?MODULE,init_connection_handler, [Role, Sock, Opts]}}) ->
2583    DefaultOpts = ssh_options:handle_options(Role,[]),
2584    ExcludedKeys = [internal_options, user_options],
2585    NonDefaultOpts =
2586        maps:filter(fun(K,V) ->
2587                            case lists:member(K,ExcludedKeys) of
2588                                true ->
2589                                    false;
2590                                false ->
2591                                    V =/= (catch maps:get(K,DefaultOpts))
2592                            end
2593                    end,
2594                    Opts),
2595    {ok, {IPp,Portp}} = inet:peername(Sock),
2596    {ok, {IPs,Ports}} = inet:sockname(Sock),
2597    [io_lib:format("Starting ~p connection:\n",[Role]),
2598     io_lib:format("Socket = ~p, Peer = ~s:~p, Local = ~s:~p,~n"
2599                   "Non-default options:~n~p",
2600                   [Sock,inet:ntoa(IPp),Portp,inet:ntoa(IPs),Ports,
2601                    NonDefaultOpts])
2602    ];
2603ssh_dbg_format(connections, F) ->
2604    ssh_dbg_format(terminate, F);
2605
2606ssh_dbg_format(connection_events, {call, {?MODULE,handle_event, [EventType, EventContent, State, _Data]}}) ->
2607    ["Connection event\n",
2608     io_lib:format("EventType: ~p~nEventContent: ~p~nState: ~p~n", [EventType, EventContent, State])
2609    ];
2610ssh_dbg_format(connection_events, {return_from, {?MODULE,handle_event,4}, Ret}) ->
2611    ["Connection event result\n",
2612     io_lib:format("~p~n", [ssh_dbg:reduce_state(Ret, #data{})])
2613    ];
2614
2615ssh_dbg_format(renegotiation, {call, {?MODULE,init_renegotiate_timers,[OldState,NewState,D]}}) ->
2616    ["Renegotiation: start timer (init_renegotiate_timers)\n",
2617     io_lib:format("State: ~p  -->  ~p~n"
2618                   "rekey_limit: ~p ({ms,bytes})~n"
2619                   "check_data_size: ~p (ms)~n",
2620                   [OldState, NewState,
2621                    ?GET_OPT(rekey_limit, (D#data.ssh_params)#ssh.opts),
2622                    ?REKEY_DATA_TIMOUT])
2623    ];
2624ssh_dbg_format(renegotiation, {return_from, {?MODULE,init_renegotiate_timers,3}, _Ret}) ->
2625    skip;
2626
2627ssh_dbg_format(renegotiation, {call, {?MODULE,renegotiate,[ConnectionHandler]}}) ->
2628    ["Renegotiation: renegotiation forced\n",
2629     io_lib:format("~p:renegotiate(~p) called~n",
2630                   [?MODULE,ConnectionHandler])
2631    ];
2632ssh_dbg_format(renegotiation, {return_from, {?MODULE,renegotiate,1}, _Ret}) ->
2633    skip;
2634
2635ssh_dbg_format(renegotiation, {call, {?MODULE,pause_renegotiate_timers,[OldState,NewState,_D]}}) ->
2636    ["Renegotiation: pause timers\n",
2637     io_lib:format("State: ~p  -->  ~p~n",
2638                   [OldState, NewState])
2639    ];
2640ssh_dbg_format(renegotiation, {return_from, {?MODULE,pause_renegotiate_timers,3}, _Ret}) ->
2641    skip;
2642
2643ssh_dbg_format(renegotiation, {call, {?MODULE,start_rekeying,[_Role,_D]}}) ->
2644    ["Renegotiation: start rekeying\n"];
2645ssh_dbg_format(renegotiation, {return_from, {?MODULE,start_rekeying,2}, _Ret}) ->
2646    skip;
2647
2648ssh_dbg_format(renegotiation, {call, {?MODULE,check_data_rekeying_dbg,[SentSinceRekey, MaxSent]}}) ->
2649    ["Renegotiation: check size of data sent\n",
2650     io_lib:format("TotalSentSinceRekey: ~p~nMaxBeforeRekey: ~p~nStartRekey: ~p~n",
2651                   [SentSinceRekey, MaxSent, SentSinceRekey >= MaxSent])
2652    ];
2653ssh_dbg_format(renegotiation, {return_from, {?MODULE,check_data_rekeying_dbg,2}, _Ret}) ->
2654    skip;
2655
2656
2657ssh_dbg_format(terminate, {call, {?MODULE,terminate, [Reason, StateName, D]}}) ->
2658    ExtraInfo =
2659        try
2660            {conn_info(peer,D),
2661             conn_info(user,D),
2662             conn_info(sockname,D)}
2663        of
2664            {{_,{IPp,Portp}}, Usr, {IPs,Ports}} when is_tuple(IPp), is_tuple(IPs),
2665                                                     is_integer(Portp), is_integer(Ports) ->
2666                io_lib:format("Peer=~s:~p, Local=~s:~p, User=~p",
2667                              [inet:ntoa(IPp),Portp,inet:ntoa(IPs),Ports,Usr]);
2668            {Peer,Usr,Sockname} ->
2669                io_lib:format("Peer=~p, Local=~p, User=~p",[Peer,Sockname,Usr])
2670        catch
2671            _:_ ->
2672                ""
2673        end,
2674    if
2675        Reason == normal ;
2676        Reason == shutdown ;
2677        element(1,Reason) == shutdown
2678        ->
2679            ["Connection Terminating:\n",
2680             io_lib:format("Reason: ~p, StateName: ~p~n~s", [Reason, StateName, ExtraInfo])
2681            ];
2682
2683        true ->
2684            ["Connection Terminating:\n",
2685             io_lib:format("Reason: ~p, StateName: ~p~n~s~nStateData = ~p",
2686                           [Reason, StateName, ExtraInfo, clean(D)])
2687            ]
2688    end;
2689ssh_dbg_format(renegotiation, {return_from, {?MODULE,terminate,3}, _Ret}) ->
2690    skip;
2691
2692ssh_dbg_format(disconnect, {call,{?MODULE,send_disconnect,
2693                                     [Code, Reason, DetailedText, Module, Line, StateName, _D]}}) ->
2694    ["Disconnecting:\n",
2695     io_lib:format(" Module = ~p, Line = ~p, StateName = ~p,~n"
2696                   " Code = ~p, Reason = ~p,~n"
2697                   " DetailedText =~n"
2698                   " ~p",
2699                   [Module, Line, StateName, Code, Reason, lists:flatten(DetailedText)])
2700    ];
2701ssh_dbg_format(renegotiation, {return_from, {?MODULE,send_disconnect,7}, _Ret}) ->
2702    skip.
2703
2704