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