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 + -
显示快捷键?