ssh_cm.erl

来自「OTP是开放电信平台的简称」· ERL 代码 · 共 1,505 行 · 第 1/3 页

ERL
1,505
字号
	      ets:insert(CTab, C#channel { recv_window_size = WSz})      end),    {noreply, State};handle_cast({close, Channel}, State) ->    #state{ssh = SSH, ctab = CTab} = State,    with_channel(State, Channel,		 fun(C) ->			 channel_close(SSH, C#channel.remote_id),			 ets:insert(CTab, C#channel{sent_close = true})		 end),    {noreply, State};handle_cast({eof, Channel}, State) ->    %%#state{ssh = SSH, ctab = _CTab} = State,    SSH = State#state.ssh,    with_channel(State, Channel,		 fun(C) ->			 channel_eof(SSH,  C#channel.remote_id)%,			 %%ets:insert(CTab, C#channel{sent_eof = true})		 end),    {noreply, State};handle_cast(_Cast, State) ->    ?dbg(true, "handle_cast: BAD cast ~p\n(State ~p)\n", [_Cast, State]),    {noreply, State}.%%--------------------------------------------------------------------%% Function: handle_info(Info, State) -> {noreply, State} |%%                                       {noreply, State, Timeout} |%%                                       {stop, Reason, State}%% Description: Handling all non call/cast messages%%--------------------------------------------------------------------handle_info({ssh_msg, SSH,#ssh_msg_service_request{name="ssh-userauth"}},	    State) when State#state.role == server->    case ssh_userauth:auth_remote(SSH, "ssh-connection", State#state.opts) of	{ok, Handle} ->	    {noreply, State#state{authhandle = Handle}};	_Error ->	    ssh_transport:disconnect(SSH, ?SSH_DISCONNECT_BY_APPLICATION),	    %{stop, {error, Error}, State}	    {stop, shutdown, State}    end;handle_info({ssh_msg, SSH, Msg}, State)                                    ->    %%SSH = State#state.ssh,    ?dbg(?DBG_SSHMSG, "handle_info<~p>: ssh_msg ~p\n", [SSH, Msg]),    case ssh_message(SSH, Msg, State) of	{disconnected, _Reason} ->	    ssh_userauth:disconnect(State#state.authhandle, State#state.opts),	    %{stop, {error, {disconnected, Reason}}, State};	    {stop, shutdown, State};	NewState ->	    {noreply, NewState}    end;handle_info({ssh_cm, Sender, Msg}, State)                                  ->    SSH = State#state.ssh,    ?dbg(?DBG_SSHCM, "handle_info<~p>: sender=~p, ssh_cm ~p\n", [SSH, Sender, Msg]),    %% only allow attached users (+ initial user)    NewState = case is_user(Sender, State) of		   false -> 		       State;		   true ->		       cm_message(SSH, Msg, State)	       end,    {noreply, NewState};handle_info({'EXIT', SSH, Reason}, State) when SSH == State#state.ssh      ->    error_logger:format("SSH_CM ~p EXIT ~p\n", [SSH, Reason]),    {noreply, State};handle_info({'EXIT', _Pid, normal}, State)                                  ->    {noreply, State};handle_info({'EXIT', _Pid, shutdown}, State)                                  ->    {noreply, State};handle_info({'EXIT', Pid, Reason}, State)                                  ->    error_logger:format("ssh_cm: Pid ~p EXIT ~p\n", [Pid, Reason]),    {noreply, State};handle_info({'DOWN', _Ref, process, Pid, normal}, State)                   ->    NewState = down_user(Pid, State),    {noreply, NewState};handle_info({'DOWN', _Ref, process, Pid, Reason}, State)                   ->    error_logger:format("Pid ~p DOWN ~p\n", [Pid, Reason]),    NewState = down_user(Pid, State),    {noreply, NewState};handle_info(_Info, State)                                                   ->    ?dbg(true, "ssh_cm:handle_info: BAD info ~p\n(State ~p)\n", [_Info, State]),    {noreply, State}.%%--------------------------------------------------------------------%% Function: %% handle_call(Request, From, State) -> {reply, Reply, State} |%%                                      {reply, Reply, State, Timeout} |%%                                      {noreply, State} |%%                                      {noreply, State, Timeout} |%%                                      {stop, Reason, Reply, State} |%%                                      {stop, Reason, State}%% Description: Handling call messages%%--------------------------------------------------------------------handle_call({connect, User, Host, Port}, _From, State) ->    case ssh_transport:connect(Host, Port, State#state.opts) of	{ok, SSH} ->	    case user_auth(SSH, State#state.opts) of		ok ->		    SSH ! {ssh_install, connect_messages()}, 		    process_flag(trap_exit, true),		    State1 = State#state{ssh=SSH},		    State2 = add_user(User, State1),  %% add inital user process		    {reply, ok, State2};		Error ->		    ssh_transport:disconnect(SSH, ?SSH_DISCONNECT_BY_APPLICATION),		    {stop, normal, Error, State}	    end;	Error ->	    {stop, normal, Error, State}    end;handle_call({attach, User}, _From, State) ->    {reply, ok, add_user(User, State)};handle_call({detach, User}, _From, State) ->    {reply, ok, del_user(User, State)};handle_call({send_window, Channel}, _From, State)                          ->    Reply = case ets:lookup(State#state.ctab, Channel) of		[C] ->		    {ok, {C#channel.send_window_size,			  C#channel.send_packet_size}};		[] -> 		    {error, einval}	    end,    {reply, Reply, State};handle_call({request, Channel, Type, Data}, From, State) ->    {noreply, do_request(Channel, Type, Data, true, From, State)};handle_call({recv_window, Channel}, _From, State) ->    Reply = case ets:lookup(State#state.ctab, Channel) of		[C] ->		    {ok, {C#channel.recv_window_size,			  C#channel.recv_packet_size}};		[] -> 		    {error, einval}	    end,    {reply, Reply, State};handle_call({set_user, Channel, User}, _From, State) ->    Reply = case is_user(User, State) of		false -> {error, einval};		true ->		    CTab = State#state.ctab,		    case ets:lookup(CTab, Channel) of			[C] ->			    ets:insert(CTab, C#channel { user = User }),			    ok;			[] -> 			    {error, einval}		    end	    end,    {reply, Reply, State};handle_call(get_authhandle, _From, State) ->    {reply, {State#state.authhandle,State#state.ssh}, State};handle_call(get_peer_addr, _From, State) ->    {reply, ssh_transport:peername(State#state.ssh), State};handle_call({set_user_ack, Channel,Ack}, _From, State) ->    CTab = State#state.ctab,    Reply = case ets:lookup(CTab, Channel) of		[C] ->		    ets:insert(CTab, C#channel { user_ack = Ack }),		    ok;		[] -> 		    {error, einval}	    end,    {reply, Reply, State};handle_call({info,User}, _From, State) ->    Result = ets:foldl(	       fun(C, Acc) when User == all; C#channel.user == User ->		       [C | Acc];		  (_, Acc) ->		       Acc	       end, [], State#state.ctab),    {reply, {ok, Result}, State};handle_call({open, User, Type, InitialWindowSize, MaxPacketSize, Data}, From, State) ->	case is_user(User, State) of	    false -> 		{reply, {error, einval}, State};	    true ->		{Channel, State1}  = new_channel_id(State),		channel_open(State#state.ssh, Type, Channel, InitialWindowSize,			     MaxPacketSize, Data),		C = #channel { type = Type,			       sys = "none",			       user = User,			       local_id = Channel,			       recv_window_size = InitialWindowSize,			       recv_packet_size = MaxPacketSize },		ets:insert(State#state.ctab, C),		State2 = add_request(true, Channel, From, State1),		{noreply, State2}	end;handle_call(stop, _From, State) ->    ssh_userauth:disconnect(State#state.authhandle, State#state.opts),    ssh_transport:close(State#state.ssh),    {stop, normal, ok, State};handle_call(_Call, _From, State) ->    ?dbg(true, "handle_call: BAD call ~p\n(State ~p)\n", [_Call, State]),    {reply, {error, bad_call}, State}.%%--------------------------------------------------------------------%% Function: terminate/2%% Description: Shutdown the server%% Returns: any (ignored by gen_server)%%--------------------------------------------------------------------terminate(_Reason, _State) ->    ok.%%--------------------------------------------------------------------%% Func: code_change/3%% Purpose: Convert process state when code is changed%% Returns: {ok, NewState}%%--------------------------------------------------------------------code_change(_OldVsn, State, _Extra) ->    {ok, State}.%%--------------------------------------------------------------------%%% Internal functions%%--------------------------------------------------------------------ssh_message(SSH, Msg, State) ->    CTab = State#state.ctab,    case Msg of	#ssh_msg_channel_open_confirmation { recipient_channel = Channel,					     sender_channel = RID,					     initial_window_size = WindowSz,					     maximum_packet_size = PacketSz					    } ->	    with_channel(State, Channel,			 fun(C) ->				 if C#channel.remote_id == undefined ->					 ets:insert(CTab, C#channel {							    remote_id = RID,							    send_window_size = WindowSz,							    send_packet_size = PacketSz });				    true ->					 ignore				 end			 end),	    reply_request(Channel, {open, Channel}, State);	#ssh_msg_channel_open_failure { recipient_channel = Channel,					reason = Reason,					description = Descr,					lang = Lang } ->	    with_channel(State, Channel,			 fun() ->				 ets:delete(CTab, Channel)			 end),	    reply_request(Channel, {open_error, Reason, Descr, Lang}, State);	#ssh_msg_channel_success { recipient_channel = Channel } ->	    reply_request(Channel, success, State);	#ssh_msg_channel_failure { recipient_channel = Channel} ->	    reply_request(Channel, failure, State);	    	#ssh_msg_channel_eof { recipient_channel = Channel} ->	    with_channel(State, Channel,			 fun(C) ->				 send_user(C, {eof, Channel})%% 				 if C#channel.sent_eof == true ->%% 					 reply_request(C#channel.local_id,%% 						       closed, State),%% 					 io:format("delete Channel b ~p\n", [Channel]),%% 					 ets:delete(CTab, Channel);%% 				    true ->%% 					 ets:insert(CTab, C#channel { recv_eof = true })%% 				 end			 end);	#ssh_msg_channel_close { recipient_channel = Channel } ->	    with_channel(State, Channel,			 fun(C) ->				 if C#channel.sent_close == false ->					 channel_close(SSH,C#channel.remote_id), 					 reply_request(C#channel.local_id,						       closed, State);				    true -> 					 ok				 end,				 ets:delete(CTab, Channel)			 end),	    reply_request(Channel, closed, State);	#ssh_msg_channel_data { recipient_channel = Channel,				data = Data } ->	    with_channel(State, Channel,			 fun(C) ->				 WSz = C#channel.recv_window_size - size(Data),				 send_user(C, {data, Channel, 0, Data}),				 ets:insert(CTab, C#channel { recv_window_size = WSz})			 end);	#ssh_msg_channel_extended_data { recipient_channel = Channel,					 data_type_code = DataType,					 data = Data} ->	    with_channel(State, Channel,			 fun(C) ->				 WSz = C#channel.recv_window_size - size(Data),				 send_user(C, {data, Channel, DataType, Data}),				 ets:insert(CTab, C#channel { recv_window_size = WSz})			 end);	#ssh_msg_channel_window_adjust { recipient_channel = Channel,					 bytes_to_add = Add } ->	    with_channel(State, Channel, 			 fun(C) -> 				 update_send_window(SSH, CTab, C, Add) 			 end);	#ssh_msg_channel_open { channel_type = Type,				sender_channel = RID,				initial_window_size = RWindowSz,				maximum_packet_size = RPacketSz,				data = Data } ->	    case Type of		"session" ->		    %% FIXME: check that we requested this !		    %% (install a listener & user somehow)%		    <<?UINT32(ALen), Address:ALen/binary, ?UINT32(Port),%		     ?UINT32(OLen), Orig:OLen/binary, ?UINT32(OrigPort)>> = Data,		    case State#state.users of			[{User,_}|_] ->			    {Channel, NewState} = new_channel_id(State),			    LWindowSz = ?DEFAULT_WINDOW_SIZE,			    LPacketSz = ?DEFAULT_PACKET_SIZE,			    C = #channel { type = Type,					   sys = "ssh",					   user = User,					   local_id = Channel,					   recv_window_size = LWindowSz,					   recv_packet_size = LPacketSz,					   send_window_size = RWindowSz,					   send_packet_size = RPacketSz,					   remote_id = RID},			    ets:insert(CTab, C),			    channel_open_confirmation(SSH, RID, Channel,						      LWindowSz, LPacketSz),			    send_user(C, {open, Channel, RID, {session}}),			    NewState;			_ ->			    channel_open_failure(SSH, RID, 						 ?SSH_OPEN_CONNECT_FAILED,						 "Connection refused", "en"),			    State		    end;		    		"forwarded-tcpip" ->		    %% FIXME: check that we requested this !		    %% (install a listener & user somehow)		    <<?UINT32(ALen), Address:ALen/binary, ?UINT32(Port),		     ?UINT32(OLen), Orig:OLen/binary, ?UINT32(OrigPort)>> = Data,		    case get_bind(Address, Port, State) of			undefined ->			    channel_open_failure(SSH, RID, 						 ?SSH_OPEN_CONNECT_FAILED,						 "Connection refused", "en"),			    State;			User ->			    {Channel, NewState} = new_channel_id(State),			    LWindowSz = ?DEFAULT_WINDOW_SIZE,			    LPacketSz = ?DEFAULT_PACKET_SIZE,			    C = #channel { type = Type,					   sys = "none",					   user = User,					   local_id = Channel,					   recv_window_size = LWindowSz,					   recv_packet_size = LPacketSz,					   send_window_size = RWindowSz,					   send_packet_size = RPacketSz },			    ets:insert(CTab, C),			    channel_open_confirmation(SSH, RID, Channel,						      LWindowSz, LPacketSz),			    send_user(C, {open, Channel, {forwarded_tcpip,							  decode_ip(Address), Port,							  decode_ip(Orig), OrigPort}}),			    NewState		    end;		_ ->		    channel_open_failure(SSH, RID, 					 ?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED,					 "Not allowed", "en"),		    State	    end;	#ssh_msg_channel_request { recipient_channel = Channel,				   request_type = Type,				   want_reply = WantReply,				   data = Data } ->	    case Type of		"exit-status" ->		    <<?UINT32(Status)>> = Data,		    send_user(CTab, Channel, {exit_status,Channel,Status});		"exit-signal" ->		    <<?UINT32(SigLen), SigName:SigLen/binary,		     ?BOOLEAN(_Core), 		     ?UINT32(ErrLen), Err:ErrLen/binary,		     ?UINT32(LangLen), Lang:LangLen/binary>> = Data,		    send_user(CTab, Channel, {exit_signal, Channel,					      binary_to_list(SigName),					      binary_to_list(Err),					      binary_to_list(Lang)});		"xon-xoff" ->		    <<?BOOLEAN(CDo)>> = Data,		    send_user(CTab, Channel, {xon_xoff, Channel, CDo=/= 0});				"window-change" ->		    <<?UINT32(Width),?UINT32(Height),		     ?UINT32(PixWidth), ?UINT32(PixHeight)>> = Data,		    send_user(CTab, Channel, {window_change,Channel,					      Width, Height,					      PixWidth, PixHeight});		"signal" ->		    <<?UINT32(SigLen), SigName:SigLen/binary>> = Data,		    send_user(CTab, Channel, {signal,Channel,					      binary_to_list(SigName)});		"subsystem" ->		    <<?UINT32(SsLen), SsName:SsLen/binary>> = Data,		    send_user(CTab, Channel, {subsystem,Channel, WantReply,					      binary_to_list(SsName)});		"pty-req" ->		    <<?UINT32(TermLen), BTermName:TermLen/binary,		     ?UINT32(Width),?UINT32(Height),		     ?UINT32(PixWidth), ?UINT32(PixHeight),		     Modes/binary>> = Data,		    TermName = binary_to_list(BTermName),		    ?dbg(?DBG_USER, "ssh_msg pty-req: TermName=~p Modes=~p\n", [TermName, Modes]),		    Pty = #ssh_pty{term = TermName,				   width = not_zero(Width, 80),				   height = not_zero(Height, 24),				   pixel_width = PixWidth,				   pixel_height = PixHeight,				   modes = decode_pty_opts(Modes)},		    send_user(CTab, Channel, {pty, Channel, WantReply, Pty});		"shell" ->		    send_user(CTab, Channel, {shell});		"exec" ->		    <<?UINT32(Len), Command:Len/binary>> = Data,		    send_user(CTab, Channel, {exec, binary_to_list(Command)});		_Other ->		    ?dbg(true, "ssh_msg ssh_msg_channel_request: Other=~p\n",			 [_Other]),		    if WantReply == true ->			    channel_failure(SSH, Channel);		       true ->			    ignore		    end	    end,	    State;	    	#ssh_msg_global_request { name = _Type,				  want_reply = WantReply,				  data = _Data } ->	    if WantReply == true ->		    request_failure(SSH);	       true ->		    ignore	    end,	    State;		#ssh_msg_disconnect { code = Code,			      description = Description,			      language = _Lang } ->	    %% close all channels	    ets:foldl(	      fun(C, _) ->		      reply_request(C#channel.local_id, closed, State)	      end, ok, CTab),	    ets:delete(CTab),	    {disconnected, {Code, Description}};	_ ->	    ?dbg(true, "ssh_connection: ~p\n", [Msg]),	    State    end.cm_message(SSH, Msg, State) ->    CTab = State#state.ctab,    case Msg of	{data, Channel, Type, Data} ->	    send_data(SSH, CTab, Channel, Type, Data),	    State;	{global_request, User, Type, WantReply, Data} ->	    case Type of		"tcpip-forward" ->		    <<?UINT32(IPLen), IP:IPLen/binary, ?UINT32(Port)>> = Data,		    State1 = add_user(User, State),  %% auto attach user		    State2 = put_bind(IP, Port, User, State1),		    send_global_request(SSH, Type, WantReply, Data),		    State2;		"cancel-tcpip-forward" ->		    <<?UINT32(IPLen), IP:IPLen/binary, ?UINT32(Port)>> = Data,		    %% note can not erase user!		    send_global_request(SSH, Type, WantReply, Data),		    del_bind(IP, Port, State);		_ ->		    send_global_request(SSH, Type, WantReply, Data),

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?