ssh_cm.erl

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

ERL
1,505
字号
%% ``The contents of this file are subject to the Erlang Public License,%% Version 1.1, (the "License"); you may not use this file except in%% compliance with the License. You should have received a copy of the%% Erlang Public License along with this software. If not, it can be%% retrieved via the world wide web at http://www.erlang.org/.%% %% Software distributed under the License is distributed on an "AS IS"%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See%% the License for the specific language governing rights and limitations%% under the License.%% %% The Initial Developer of the Original Code is Ericsson Utvecklings AB.%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings%% AB. All Rights Reserved.''%% %%     $Id$%%%%% Description : SSH connection protocol manager-module(ssh_cm).-include("ssh.hrl").-include("ssh_connect.hrl").-define(DEFAULT_PACKET_SIZE, 32768).-define(DEFAULT_WINDOW_SIZE, 2*?DEFAULT_PACKET_SIZE).-define(DEFAULT_TIMEOUT, 5000).-behaviour(gen_server).-import(lists, [reverse/1, foreach/2]).%% gen_server callbacks-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2,	 code_change/3]).-export([connect/1, connect/2, connect/3]).-export([listen/2, listen/3, listen/4, stop_listener/1]).-export([stop/1]).%%-export([dist_start/1, dist_start/2]).-export([encode_ip/1]).%% API-export([adjust_window/3, attach/2, detach/2,	 tcpip_forward/3, cancel_tcpip_forward/3, direct_tcpip/6,	 direct_tcpip/8, 	 close/2,	 shell/2, exec/4, i/1, i/2, info/1, info/2, 	 recv_window/3, send/3, send/4, renegotiate/1, renegotiate/2,	 request_success/2, send_ack/3, send_ack/4, send_ack/5, send_eof/2,	 send_window/3, session_open/2, session_open/4, subsystem/4,	 open_pty/3, open_pty/7, open_pty/9,	 set_user_ack/4, set_user/4,	 setenv/5, signal/3, winch/4,	 get_authhandle/1,	 get_peer_addr/1]).%% Special for ssh_userauth (and similar)%%-export([set_ssh_msg_handler/2, reset_ssh_msg_handler/1]).%% internal exports%% -export([listen_init/7, connect_init/6]).-define(DBG_SSHMSG, true).-define(DBG_SSHCM,  true).-define(DBG_USER,   true).-record(channel,	{	  type,          %% "session", "x11", "forwarded-tcpip", "direct-tcpip"	  sys,           %% "none", "shell", "exec" "subsystem"	  user,          %% "user" process id (default to cm user)	  user_ack = false,   %% user want ack packet when data is sent	  local_id,           %% local channel id	  recv_window_size,	  recv_packet_size,	  %%recv_eof = false,	  recv_close = false,	  remote_id,          %% remote channel id	  send_window_size,	  send_packet_size,	  %%sent_eof = false,	  sent_close = false,	  send_buf = []	 }).-record(state,	{	  role,	  %%ssh_msg_handler,	  ssh,	  ctab,	  binds = [],	  users = [],	  channel_id = 0,	  opts,	  requests = [], %% [{Channel, Pid}...] awaiting reply on request	  authhandle     %% for session termination	 }).%%====================================================================%% API%%====================================================================%%--------------------------------------------------------------------%% Function: connect(...) -> {ok,Pid} | {error,Error}%% Description: Starts the server (as an ssh-client)%%--------------------------------------------------------------------connect(Host) ->    connect(Host, []).connect(Host, Opts) ->    connect(Host, ?SSH_DEFAULT_PORT, Opts).connect(Host, Port, Opts) ->    {ok, CM} = gen_server:start_link(?MODULE, [client, Opts], []),    %% Timeout shall be infinity, OTP-6488. This call to an internal process    %% will get an answer unless the process dies!     case gen_server:call(CM, {connect, self(), Host, Port}, infinity) of	ok ->	    {ok, CM};	Error ->	    {error, Error}    end.%%--------------------------------------------------------------------%% Function: listen(...) -> Pid | {error,Error}%% Description: Starts a listening server (as an ssh-server)%%--------------------------------------------------------------------listen(UserFun, Port) ->    listen(UserFun, Port, []).listen(UserFun, Port, Opts) ->    listen(UserFun, any, Port, Opts).listen(UserFun, Addr, Port, Opts) ->    Self = self(),    ssh_userauth:reg_user_auth_server(),    ssh_transport:listen(      fun(SSH) ->	      {ok, CM} =		  gen_server:start_link(		    ?MODULE, [server, Self, UserFun, SSH, Opts], []),	      CM      end, Addr, Port, Opts).%%--------------------------------------------------------------------%% Function: stop_listener(Pid) -> ok%% Description: Stops the listener%%--------------------------------------------------------------------stop_listener(Pid) ->    ssh_transport:stop_listener(Pid).%% %%%% %% special ssh distribution version%% %%%% dist_start(Node) ->%%     Opts1 = case init:get_argument('ssh_password') of%% 	       {ok, [[Passwd]]} -> [{password, Passwd}];%% 	       error -> []%% 	   end,%%     Opts2 = case init:get_argument('ssh_user') of%% 		{ok, [[User]]} -> [{user, User}];%% 		error -> []%% 	    end,%%     dist_start(Node, Opts1++Opts2).    %% dist_start(Node, Opts) when atom(Node), list(Opts) ->%%     case string:tokens(atom_to_list(Node), "@") of%% 	[_, "ssh:"++Host] ->%% 	    CMHost = list_to_atom(Host),%% 	    case whereis(CMHost) of%% 		undefined ->%% 		    start(CMHost, Host, Opts);%% 		Pid when pid(Pid) ->%% 		    {ok,Pid};%% 		_ ->%% 		    {error, einval}%% 	    end;%% 	_ ->%% 	    {error, einval}%%     end;%% dist_start(_, _) ->%%     {error, einval}.%%====================================================================%% gen_server callbacks%%====================================================================%%--------------------------------------------------------------------%% Function: init(Args) -> {ok, State} |%%                         {ok, State, Timeout} |%%                         ignore               |%%                         {stop, Reason}%% Description: Initiates the server%%--------------------------------------------------------------------init([server, _Caller, UserFun, SSH, Opts]) ->    SSH ! {ssh_install, connect_messages()},    process_flag(trap_exit, true),    User = UserFun(),    %% Caller ! {self(), {ok, self()}},    CTab = ets:new(cm_tab, [set,{keypos, #channel.local_id}]),    State = #state{role = server, ctab = CTab, ssh = SSH, opts = Opts,		   requests = []},    NewState = add_user(User, State),  %% add inital user    {ok, NewState};init([client, Opts]) ->    CTab = ets:new(cm_tab, [set,{keypos,#channel.local_id}]),    State = #state{role = client, ctab = CTab, ssh = undefined,		   opts = Opts, requests = []},    {ok, State}.i(CM) ->    i(CM, all).i(CM, User) ->    case info(CM, User) of	{ok, Cs} ->	    Cs1 = lists:keysort(#channel.user, Cs),	    foreach(	      fun(C) ->		      io:format("~10p ~w ~s/~s ~w/~w ~w/~w\n",				[C#channel.user,				 C#channel.local_id,				 C#channel.type, C#channel.sys,				 C#channel.recv_window_size,				 C#channel.recv_packet_size,				 C#channel.send_window_size,				 C#channel.send_packet_size])	      end, Cs1);	Error ->	    Error    end.    info(CM) ->    info(CM, all).info(CM, User) ->    gen_server:call(CM, {info, User}).%% CM Client commandssession_open(CM, TMO) ->    session_open(CM, ?DEFAULT_WINDOW_SIZE, ?DEFAULT_PACKET_SIZE, TMO).session_open(CM, InitialWindowSize, MaxPacketSize, TMO) ->    case gen_server:call(CM, {open, self(), "session",			      InitialWindowSize, MaxPacketSize, <<>>}, TMO) of	{open, C} -> {ok, C};	Error -> Error    end.direct_tcpip(CM, RemoteHost, RemotePort, OrigIP, OrigPort, TMO) ->    direct_tcpip(CM, RemoteHost, RemotePort, OrigIP, OrigPort,		 ?DEFAULT_WINDOW_SIZE, ?DEFAULT_PACKET_SIZE, TMO).direct_tcpip(CM, RemoteIP, RemotePort, OrigIP, OrigPort,	     InitialWindowSize, MaxPacketSize, TMO) ->    case {encode_ip(RemoteIP), encode_ip(OrigIP)} of	{false, _} -> {error, einval};	{_, false} -> {error, einval};	{RIP, OIP} ->	    gen_server:call(CM, {open, self(), "direct-tcpip", 				 InitialWindowSize, MaxPacketSize,				 [?string(RIP), ?uint32(RemotePort),				  ?string(OIP), ?uint32(OrigPort)] }, TMO)%%% 	    receive%%% 		{ssh_cm, CM, {open, Channel}} ->%%% 		    {ok, Channel};%%% 		{ssh_cm, CM, {open_error, _Reason, Descr, _Lang}} ->%%% 		    {error, Descr}%%% 	    end    end.tcpip_forward(CM, BindIP, BindPort)                                        ->    case encode_ip(BindIP) of	false -> {error, einval};	IPStr ->	    global_request(CM, "tcpip-forward", true,			   [?string(IPStr),			    ?uint32(BindPort)])    end.cancel_tcpip_forward(CM, BindIP, Port) ->    case encode_ip(BindIP) of	false -> {error, einval};	IPStr ->	    global_request(CM, "cancel-tcpip-forward", true,			   [?string(IPStr),			    ?uint32(Port)])    end.open_pty(CM, Channel, TMO) ->    open_pty(CM, Channel, os:getenv("TERM"), 80, 24, [], TMO).open_pty(CM, Channel, Term, Width, Height, PtyOpts, TMO) ->    open_pty(CM, Channel, Term, Width, Height, 0, 0, PtyOpts, TMO).open_pty(CM, Channel, Term, Width, Height, PixWidth, PixHeight, PtyOpts, TMO) ->    request(CM, Channel, "pty-req", true, 	    [?string(Term),	     ?uint32(Width), ?uint32(Height),	     ?uint32(PixWidth),?uint32(PixHeight),	     encode_pty_opts(PtyOpts)], TMO).setenv(CM, Channel, Var, Value, TMO) ->    request(CM, Channel, "env", true, [?string(Var), ?string(Value)], TMO).shell(CM, Channel) ->    request(CM, Channel, "shell", false, <<>>, 0).exec(CM, Channel, Command, TMO) ->    request(CM, Channel, "exec", true, [?string(Command)], TMO).subsystem(CM, Channel, SubSystem, TMO) ->    request(CM, Channel, "subsystem", true, [?string(SubSystem)], TMO).winch(CM, Channel, Width, Height) ->    winch(CM, Channel, Width, Height, 0, 0).winch(CM, Channel, Width, Height, PixWidth, PixHeight)                     ->    request(CM, Channel, "window-change", false, 	    [?uint32(Width), ?uint32(Height),	     ?uint32(PixWidth), ?uint32(PixHeight)], 0).signal(CM, Channel, Sig)                                                   ->    request(CM, Channel, "signal", false,	    [?string(Sig)], 0).attach(CM, TMO) ->    gen_server:call(CM, {attach, self()}, TMO).detach(CM, TMO) ->    gen_server:call(CM, {detach, self()}, TMO).renegotiate(CM) ->    renegotiate(CM,[]).renegotiate(CM,Opts) ->    gen_server:cast(CM, {renegotiate,Opts}).%% Setup user ack on data messages (i.e signal when the data has been sent)set_user_ack(CM, Channel, Ack, TMO) ->    gen_server:call(CM, {set_user_ack, Channel, Ack}, TMO).get_authhandle(CM) ->    gen_server:call(CM, get_authhandle).get_peer_addr(CM) ->    gen_server:call(CM, get_peer_addr).set_user(CM, Channel, User, TMO) ->    gen_server:call(CM, {set_user, Channel, User}, TMO).send_window(CM, Channel, TMO) ->    gen_server:call(CM, {send_window, Channel}, TMO).recv_window(CM, Channel, TMO) ->    gen_server:call(CM, {recv_window, Channel}, TMO).adjust_window(CM, Channel, Bytes) ->    gen_server:cast(CM, {adjust_window, Channel, Bytes}).close(CM, Channel) ->    gen_server:cast(CM, {close, Channel}).stop(CM) ->    gen_server:call(CM, stop).send_eof(CM, Channel)                                                      ->    gen_server:cast(CM, {eof, Channel}).send(CM, Channel, Data)                                                    ->    CM ! {ssh_cm, self(), {data, Channel, 0, Data}}.send(CM, Channel, Type, Data)                                              ->    CM ! {ssh_cm, self(), {data, Channel, Type, Data}}.send_ack(CM, Channel, Data) ->    send_ack(CM, Channel, 0, Data, infinity).send_ack(CM, Channel, Type, Data) ->    send_ack(CM, Channel, Type, Data, infinity).send_ack(CM, Channel, Type, Data, Timeout) ->    send(CM, Channel, Type, Data),    receive	{ssh_cm, CM, {ack, Channel}} ->	    ok    after Timeout ->	    {error, timeout}    end.request(CM, Channel, Type, Reply, Data, TMO) ->    case Reply of	true -> gen_server:call(CM, {request, Channel, Type, Data}, TMO);	false -> gen_server:cast(CM, {request, Channel, Type, Data})    end.global_request(CM, Type, Reply, Data) ->    CM ! {ssh_cm, self(), {global_request,self(),Type,Reply,Data}},    if Reply == true ->	    receive		{ssh_cm, CM, {success, _Channel}} ->		    ok;		{ssh_cm, CM, {failure, _Channel}} ->		    error	    end;       true ->	    ok    end.%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% CM command encode/decode table%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%connect_messages() ->    [ {ssh_msg_global_request, ?SSH_MSG_GLOBAL_REQUEST,       [string, 	boolean,	'...']},      {ssh_msg_request_success, ?SSH_MSG_REQUEST_SUCCESS,       ['...']},      {ssh_msg_request_failure, ?SSH_MSG_REQUEST_FAILURE,       []},            {ssh_msg_channel_open, ?SSH_MSG_CHANNEL_OPEN,       [string,	uint32,	uint32,	uint32,	'...']},      {ssh_msg_channel_open_confirmation, ?SSH_MSG_CHANNEL_OPEN_CONFIRMATION,       [uint32,	uint32,	uint32,	uint32,	'...']},      {ssh_msg_channel_open_failure, ?SSH_MSG_CHANNEL_OPEN_FAILURE,       [uint32,	uint32,	string,	string]},      {ssh_msg_channel_window_adjust, ?SSH_MSG_CHANNEL_WINDOW_ADJUST,       [uint32,	uint32]},      {ssh_msg_channel_data, ?SSH_MSG_CHANNEL_DATA,       [uint32,	binary]},      {ssh_msg_channel_extended_data, ?SSH_MSG_CHANNEL_EXTENDED_DATA,       [uint32,	uint32,	binary]},      {ssh_msg_channel_eof, ?SSH_MSG_CHANNEL_EOF,       [uint32]},      {ssh_msg_channel_close, ?SSH_MSG_CHANNEL_CLOSE,       [uint32]},      {ssh_msg_channel_request, ?SSH_MSG_CHANNEL_REQUEST,       [uint32,	string,	boolean,	'...']},      {ssh_msg_channel_success, ?SSH_MSG_CHANNEL_SUCCESS,       [uint32]},      {ssh_msg_channel_failure, ?SSH_MSG_CHANNEL_FAILURE,       [uint32]}     ].%%--------------------------------------------------------------------%% Function: handle_cast/2%% Description: Handling cast messages%% Returns: {noreply, State}          |%%          {noreply, State, Timeout} |%%          {stop, Reason, State}            (terminate/2 is called)%%--------------------------------------------------------------------handle_cast({request, Channel, Type, Data}, State) ->    {noreply, do_request(Channel, Type, Data, false, undefined, State)};handle_cast({renegotiate, Opts}, State) ->    State#state.ssh ! {ssh_renegotiate, false, Opts},    {noreply, State};handle_cast({adjust_window, Channel, Bytes}, State) ->    #state{ssh = SSH, ctab = CTab} = State,    with_channel(      State, Channel,      fun(C) ->	      WSz = C#channel.recv_window_size + Bytes,	      channel_adjust_window(SSH, C#channel.remote_id, Bytes),

⌨️ 快捷键说明

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