1%% 2%% %CopyrightBegin% 3%% 4%% Copyright Ericsson AB 2008-2018. 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 23%%---------------------------------------------------------------------- 24%% Purpose: Details of connection protocol 25%%---------------------------------------------------------------------- 26 27-module(ssh_connection). 28 29-include("ssh.hrl"). 30-include("ssh_connect.hrl"). 31-include("ssh_transport.hrl"). 32 33%% API 34-export([session_channel/2, session_channel/4, 35 exec/4, shell/2, subsystem/4, send/3, send/4, send/5, 36 send_eof/2, adjust_window/3, setenv/5, close/2, reply_request/4, 37 ptty_alloc/3, ptty_alloc/4]). 38 39%% Potential API currently unsupported and not tested 40-export([window_change/4, window_change/6, 41 signal/3, exit_status/3]). 42 43%% Internal SSH application API 44-export([channel_data/5, 45 handle_msg/3, 46 handle_stop/1, 47 48 channel_adjust_window_msg/2, 49 channel_close_msg/1, 50 channel_open_failure_msg/4, 51 channel_open_msg/5, 52 channel_status_msg/1, 53 channel_data_msg/3, 54 channel_eof_msg/1, 55 channel_failure_msg/1, 56 channel_open_confirmation_msg/4, 57 channel_request_msg/4, 58 channel_success_msg/1, 59 60 request_failure_msg/0, 61 request_success_msg/1, 62 63 bind/4, unbind/3, unbind_channel/2, 64 bound_channel/3, encode_ip/1 65 ]). 66 67-type connection_ref() :: ssh:connection_ref(). 68-type channel_id() :: ssh:channel_id(). 69 70%%-------------------------------------------------------------------- 71%%% API 72%%-------------------------------------------------------------------- 73 74%%-------------------------------------------------------------------- 75%% Description: Opens a channel for a ssh session. A session is a 76%% remote execution of a program. The program may be a shell, an 77%% application, a system command, or some built-in subsystem. 78%% -------------------------------------------------------------------- 79 80-spec session_channel(connection_ref(), timeout()) -> 81 {ok, channel_id()} | {error, timeout | closed}. 82 83session_channel(ConnectionHandler, Timeout) -> 84 session_channel(ConnectionHandler, ?DEFAULT_WINDOW_SIZE, ?DEFAULT_PACKET_SIZE, Timeout). 85 86-spec session_channel(connection_ref(), integer(), integer(), timeout()) -> 87 {ok, channel_id()} | {error, timeout | closed}. 88 89session_channel(ConnectionHandler, InitialWindowSize, MaxPacketSize, Timeout) -> 90 case ssh_connection_handler:open_channel(ConnectionHandler, "session", <<>>, 91 InitialWindowSize, 92 MaxPacketSize, Timeout) of 93 {open, Channel} -> 94 {ok, Channel}; 95 Error -> 96 Error 97 end. 98 99%%-------------------------------------------------------------------- 100%% Description: Will request that the server start the 101%% execution of the given command. 102%%-------------------------------------------------------------------- 103-spec exec(connection_ref(), channel_id(), string(), timeout()) -> 104 success | failure | {error, timeout | closed}. 105 106exec(ConnectionHandler, ChannelId, Command, TimeOut) -> 107 ssh_connection_handler:request(ConnectionHandler, self(), ChannelId, "exec", 108 true, [?string(Command)], TimeOut). 109 110%%-------------------------------------------------------------------- 111%% Description: Will request that the user's default shell (typically 112%% defined in /etc/passwd in UNIX systems) be started at the other 113%% end. 114%%-------------------------------------------------------------------- 115-spec shell(connection_ref(), channel_id()) -> 116 ok | success | failure | {error, timeout}. 117 118shell(ConnectionHandler, ChannelId) -> 119 ssh_connection_handler:request(ConnectionHandler, self(), ChannelId, 120 "shell", false, <<>>, 0). 121%%-------------------------------------------------------------------- 122%% 123%% Description: Executes a predefined subsystem. 124%%-------------------------------------------------------------------- 125-spec subsystem(connection_ref(), channel_id(), string(), timeout()) -> 126 success | failure | {error, timeout | closed}. 127 128subsystem(ConnectionHandler, ChannelId, SubSystem, TimeOut) -> 129 ssh_connection_handler:request(ConnectionHandler, self(), 130 ChannelId, "subsystem", 131 true, [?string(SubSystem)], TimeOut). 132%%-------------------------------------------------------------------- 133%% Description: Sends channel data. 134%%-------------------------------------------------------------------- 135-spec send(connection_ref(), channel_id(), iodata()) -> 136 ok | {error, timeout | closed}. 137send(ConnectionHandler, ChannelId, Data) -> 138 send(ConnectionHandler, ChannelId, 0, Data, infinity). 139 140 141-spec send(connection_ref(), channel_id(), integer()| iodata(), timeout() | iodata()) -> 142 ok | {error, timeout | closed}. 143 144send(ConnectionHandler, ChannelId, Data, TimeOut) when is_integer(TimeOut) -> 145 send(ConnectionHandler, ChannelId, 0, Data, TimeOut); 146 147send(ConnectionHandler, ChannelId, Data, infinity) -> 148 send(ConnectionHandler, ChannelId, 0, Data, infinity); 149 150send(ConnectionHandler, ChannelId, Type, Data) -> 151 send(ConnectionHandler, ChannelId, Type, Data, infinity). 152 153 154-spec send(connection_ref(), channel_id(), integer(), iodata(), timeout()) -> 155 ok | {error, timeout | closed}. 156 157send(ConnectionHandler, ChannelId, Type, Data, TimeOut) -> 158 ssh_connection_handler:send(ConnectionHandler, ChannelId, 159 Type, Data, TimeOut). 160%%-------------------------------------------------------------------- 161-spec send_eof(connection_ref(), channel_id()) -> ok | {error, closed}. 162%% 163%% 164%% Description: Sends eof on the channel <ChannelId>. 165%%-------------------------------------------------------------------- 166send_eof(ConnectionHandler, Channel) -> 167 ssh_connection_handler:send_eof(ConnectionHandler, Channel). 168 169%%-------------------------------------------------------------------- 170-spec adjust_window(connection_ref(), channel_id(), integer()) -> ok. 171%% 172%% 173%% Description: Adjusts the ssh flowcontrol window. 174%%-------------------------------------------------------------------- 175adjust_window(ConnectionHandler, Channel, Bytes) -> 176 ssh_connection_handler:adjust_window(ConnectionHandler, Channel, Bytes). 177 178%%-------------------------------------------------------------------- 179-spec setenv(connection_ref(), channel_id(), string(), string(), timeout()) -> 180 success | failure | {error, timeout | closed}. 181%% 182%% 183%% Description: Environment variables may be passed to the shell/command to be 184%% started later. 185%%-------------------------------------------------------------------- 186setenv(ConnectionHandler, ChannelId, Var, Value, TimeOut) -> 187 ssh_connection_handler:request(ConnectionHandler, ChannelId, 188 "env", true, [?string(Var), ?string(Value)], TimeOut). 189 190 191%%-------------------------------------------------------------------- 192-spec close(connection_ref(), channel_id()) -> ok. 193%% 194%% 195%% Description: Sends a close message on the channel <ChannelId>. 196%%-------------------------------------------------------------------- 197close(ConnectionHandler, ChannelId) -> 198 ssh_connection_handler:close(ConnectionHandler, ChannelId). 199 200%%-------------------------------------------------------------------- 201-spec reply_request(connection_ref(), boolean(), success | failure, channel_id()) -> ok. 202%% 203%% 204%% Description: Send status replies to requests that want such replies. 205%%-------------------------------------------------------------------- 206reply_request(ConnectionHandler, true, Status, ChannelId) -> 207 ssh_connection_handler:reply_request(ConnectionHandler, Status, ChannelId); 208reply_request(_,false, _, _) -> 209 ok. 210 211%%-------------------------------------------------------------------- 212%% Description: Sends a ssh connection protocol pty_req. 213%%-------------------------------------------------------------------- 214-spec ptty_alloc(connection_ref(), channel_id(), proplists:proplist()) -> 215 success | failure | {error, timeout}. 216 217ptty_alloc(ConnectionHandler, Channel, Options) -> 218 ptty_alloc(ConnectionHandler, Channel, Options, infinity). 219 220 221-spec ptty_alloc(connection_ref(), channel_id(), proplists:proplist(), timeout()) -> 222 success | failure | {error, timeout | closed}. 223 224ptty_alloc(ConnectionHandler, Channel, Options0, TimeOut) -> 225 TermData = backwards_compatible(Options0, []), % FIXME 226 {Width, PixWidth} = pty_default_dimensions(width, TermData), 227 {Height, PixHeight} = pty_default_dimensions(height, TermData), 228 pty_req(ConnectionHandler, Channel, 229 proplists:get_value(term, TermData, os:getenv("TERM", ?DEFAULT_TERMINAL)), 230 proplists:get_value(width, TermData, Width), 231 proplists:get_value(height, TermData, Height), 232 proplists:get_value(pixel_widh, TermData, PixWidth), 233 proplists:get_value(pixel_height, TermData, PixHeight), 234 proplists:get_value(pty_opts, TermData, []), TimeOut 235 ). 236%%-------------------------------------------------------------------- 237%% Not yet officialy supported! The following functions are part of the 238%% initial contributed ssh application. They are untested. Do we want them? 239%% Should they be documented and tested? 240%%-------------------------------------------------------------------- 241window_change(ConnectionHandler, Channel, Width, Height) -> 242 window_change(ConnectionHandler, Channel, Width, Height, 0, 0). 243window_change(ConnectionHandler, Channel, Width, Height, 244 PixWidth, PixHeight) -> 245 ssh_connection_handler:request(ConnectionHandler, Channel, 246 "window-change", false, 247 [?uint32(Width), ?uint32(Height), 248 ?uint32(PixWidth), ?uint32(PixHeight)], 0). 249 250signal(ConnectionHandler, Channel, Sig) -> 251 ssh_connection_handler:request(ConnectionHandler, Channel, 252 "signal", false, [?string(Sig)], 0). 253 254 255exit_status(ConnectionHandler, Channel, Status) -> 256 ssh_connection_handler:request(ConnectionHandler, Channel, 257 "exit-status", false, [?uint32(Status)], 0). 258 259%%-------------------------------------------------------------------- 260%%% Internal, that is, ssh application internal API 261%%-------------------------------------------------------------------- 262 263%%%---------------------------------------------------------------- 264%%% Send data on a channel/connection as result of for example 265%%% ssh_connection:send (executed in the ssh_connection_state machine) 266%%% 267 268channel_data(ChannelId, DataType, Data0, 269 #connection{channel_cache = Cache} = Connection, 270 From) -> 271 case ssh_client_channel:cache_lookup(Cache, ChannelId) of 272 #channel{remote_id = Id, sent_close = false} = Channel0 -> 273 Data = 274 try iolist_to_binary(Data0) 275 catch 276 _:_ -> 277 unicode:characters_to_binary(Data0) 278 end, 279 {SendList, Channel} = 280 update_send_window(Channel0#channel{flow_control = From}, DataType, 281 Data, Connection), 282 Replies = 283 lists:map(fun({SendDataType, SendData}) -> 284 {connection_reply, 285 channel_data_msg(Id, 286 SendDataType, 287 SendData)} 288 end, SendList), 289 FlowCtrlMsgs = flow_control(Replies, Channel, Cache), 290 {Replies ++ FlowCtrlMsgs, Connection}; 291 _ -> 292 {[{channel_request_reply,From,{error,closed}}], Connection} 293 end. 294 295%%%---------------------------------------------------------------- 296%%% Handle the channel messages on behalf of the ssh_connection_handler 297%%% state machine. 298%%% 299%%% Replies {Reply, UpdatedConnection} 300%%% 301 302handle_msg(#ssh_msg_channel_open_confirmation{recipient_channel = ChannelId, 303 sender_channel = RemoteId, 304 initial_window_size = WindowSz, 305 maximum_packet_size = PacketSz}, 306 #connection{channel_cache = Cache} = Connection0, _) -> 307 308 #channel{remote_id = undefined} = Channel = 309 ssh_client_channel:cache_lookup(Cache, ChannelId), 310 311 ssh_client_channel:cache_update(Cache, Channel#channel{ 312 remote_id = RemoteId, 313 recv_packet_size = max(32768, % rfc4254/5.2 314 min(PacketSz, Channel#channel.recv_packet_size) 315 ), 316 send_window_size = WindowSz, 317 send_packet_size = PacketSz}), 318 reply_msg(Channel, Connection0, {open, ChannelId}); 319 320handle_msg(#ssh_msg_channel_open_failure{recipient_channel = ChannelId, 321 reason = Reason, 322 description = Descr, 323 lang = Lang}, 324 #connection{channel_cache = Cache} = Connection0, _) -> 325 Channel = ssh_client_channel:cache_lookup(Cache, ChannelId), 326 ssh_client_channel:cache_delete(Cache, ChannelId), 327 reply_msg(Channel, Connection0, {open_error, Reason, Descr, Lang}); 328 329handle_msg(#ssh_msg_channel_success{recipient_channel = ChannelId}, Connection, _) -> 330 reply_msg(ChannelId, Connection, success); 331 332handle_msg(#ssh_msg_channel_failure{recipient_channel = ChannelId}, Connection, _) -> 333 reply_msg(ChannelId, Connection, failure); 334 335handle_msg(#ssh_msg_channel_eof{recipient_channel = ChannelId}, Connection, _) -> 336 reply_msg(ChannelId, Connection, {eof, ChannelId}); 337 338handle_msg(#ssh_msg_channel_close{recipient_channel = ChannelId}, 339 #connection{channel_cache = Cache} = Connection0, _) -> 340 341 case ssh_client_channel:cache_lookup(Cache, ChannelId) of 342 #channel{sent_close = Closed, remote_id = RemoteId, 343 flow_control = FlowControl} = Channel -> 344 ssh_client_channel:cache_delete(Cache, ChannelId), 345 {CloseMsg, Connection} = 346 reply_msg(Channel, Connection0, {closed, ChannelId}), 347 ConnReplyMsgs = 348 case Closed of 349 true -> []; 350 false -> 351 RemoteCloseMsg = channel_close_msg(RemoteId), 352 [{connection_reply, RemoteCloseMsg}] 353 end, 354 355 %% if there was a send() in progress, make it fail 356 SendReplyMsgs = 357 case FlowControl of 358 undefined -> []; 359 From -> 360 [{flow_control, From, {error, closed}}] 361 end, 362 363 Replies = ConnReplyMsgs ++ CloseMsg ++ SendReplyMsgs, 364 {Replies, Connection}; 365 366 undefined -> 367 {[], Connection0} 368 end; 369 370handle_msg(#ssh_msg_channel_data{recipient_channel = ChannelId, 371 data = Data}, 372 Connection, _) -> 373 channel_data_reply_msg(ChannelId, Connection, 0, Data); 374 375handle_msg(#ssh_msg_channel_extended_data{recipient_channel = ChannelId, 376 data_type_code = DataType, 377 data = Data}, 378 Connection, _) -> 379 channel_data_reply_msg(ChannelId, Connection, DataType, Data); 380 381handle_msg(#ssh_msg_channel_window_adjust{recipient_channel = ChannelId, 382 bytes_to_add = Add}, 383 #connection{channel_cache = Cache} = Connection, _) -> 384 #channel{send_window_size = Size, remote_id = RemoteId} = 385 Channel0 = ssh_client_channel:cache_lookup(Cache, ChannelId), 386 387 {SendList, Channel} = %% TODO: Datatype 0 ? 388 update_send_window(Channel0#channel{send_window_size = Size + Add}, 389 0, undefined, Connection), 390 391 Replies = lists:map(fun({Type, Data}) -> 392 {connection_reply, channel_data_msg(RemoteId, Type, Data)} 393 end, SendList), 394 FlowCtrlMsgs = flow_control(Channel, Cache), 395 {Replies ++ FlowCtrlMsgs, Connection}; 396 397handle_msg(#ssh_msg_channel_open{channel_type = "session" = Type, 398 sender_channel = RemoteId, 399 initial_window_size = WindowSz, 400 maximum_packet_size = PacketSz}, 401 #connection{options = SSHopts} = Connection0, 402 server) -> 403 MinAcceptedPackSz = 404 ?GET_OPT(minimal_remote_max_packet_size, SSHopts), 405 406 if 407 MinAcceptedPackSz =< PacketSz -> 408 try setup_session(Connection0, RemoteId, 409 Type, WindowSz, PacketSz) of 410 Result -> 411 Result 412 catch _:_ -> 413 FailMsg = channel_open_failure_msg(RemoteId, 414 ?SSH_OPEN_CONNECT_FAILED, 415 "Connection refused", "en"), 416 {[{connection_reply, FailMsg}], Connection0} 417 end; 418 419 MinAcceptedPackSz > PacketSz -> 420 FailMsg = channel_open_failure_msg(RemoteId, 421 ?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, 422 lists:concat(["Maximum packet size below ",MinAcceptedPackSz, 423 " not supported"]), "en"), 424 {[{connection_reply, FailMsg}], Connection0} 425 end; 426 427handle_msg(#ssh_msg_channel_open{channel_type = "session", 428 sender_channel = RemoteId}, 429 Connection, 430 client) -> 431 %% Client implementations SHOULD reject any session channel open 432 %% requests to make it more difficult for a corrupt server to attack the 433 %% client. See See RFC 4254 6.1. 434 FailMsg = channel_open_failure_msg(RemoteId, 435 ?SSH_OPEN_CONNECT_FAILED, 436 "Connection refused", "en"), 437 {[{connection_reply, FailMsg}], Connection}; 438 439handle_msg(#ssh_msg_channel_open{sender_channel = RemoteId}, Connection, _) -> 440 FailMsg = channel_open_failure_msg(RemoteId, 441 ?SSH_OPEN_ADMINISTRATIVELY_PROHIBITED, 442 "Not allowed", "en"), 443 {[{connection_reply, FailMsg}], Connection}; 444 445handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, 446 request_type = "exit-status", 447 data = Data}, 448 Connection, _) -> 449 <<?UINT32(Status)>> = Data, 450 reply_msg(ChannelId, Connection, {exit_status, ChannelId, Status}); 451 452handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, 453 request_type = "exit-signal", 454 want_reply = false, 455 data = Data}, 456 #connection{channel_cache = Cache} = Connection0, _) -> 457 <<?DEC_BIN(SigName, _SigLen), 458 ?BOOLEAN(_Core), 459 ?DEC_BIN(Err, _ErrLen), 460 ?DEC_BIN(Lang, _LangLen)>> = Data, 461 Channel = ssh_client_channel:cache_lookup(Cache, ChannelId), 462 RemoteId = Channel#channel.remote_id, 463 {Reply, Connection} = reply_msg(Channel, Connection0, 464 {exit_signal, ChannelId, 465 binary_to_list(SigName), 466 binary_to_list(Err), 467 binary_to_list(Lang)}), 468 CloseMsg = channel_close_msg(RemoteId), 469 {[{connection_reply, CloseMsg}|Reply], Connection}; 470 471handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, 472 request_type = "xon-xoff", 473 want_reply = false, 474 data = Data}, 475 Connection, _) -> 476 <<?BOOLEAN(CDo)>> = Data, 477 reply_msg(ChannelId, Connection, {xon_xoff, ChannelId, CDo=/= 0}); 478 479handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, 480 request_type = "window-change", 481 want_reply = false, 482 data = Data}, 483 Connection0, _) -> 484 <<?UINT32(Width),?UINT32(Height), 485 ?UINT32(PixWidth), ?UINT32(PixHeight)>> = Data, 486 reply_msg(ChannelId, Connection0, {window_change, ChannelId, 487 Width, Height, 488 PixWidth, PixHeight}); 489 490handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, 491 request_type = "signal", 492 data = Data}, 493 Connection0, _) -> 494 <<?DEC_BIN(SigName, _SigLen)>> = Data, 495 reply_msg(ChannelId, Connection0, {signal, ChannelId, 496 binary_to_list(SigName)}); 497 498handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, 499 request_type = "subsystem", 500 want_reply = WantReply, 501 data = Data}, 502 #connection{channel_cache = Cache} = Connection, server) -> 503 <<?DEC_BIN(SsName,_SsLen)>> = Data, 504 #channel{remote_id=RemoteId} = Channel = 505 ssh_client_channel:cache_lookup(Cache, ChannelId), 506 Reply = 507 try 508 start_subsystem(SsName, Connection, Channel, 509 {subsystem, ChannelId, WantReply, binary_to_list(SsName)}) 510 of 511 {ok, Pid} -> 512 erlang:monitor(process, Pid), 513 ssh_client_channel:cache_update(Cache, Channel#channel{user=Pid}), 514 channel_success_msg(RemoteId); 515 {error,_Error} -> 516 channel_failure_msg(RemoteId) 517 catch 518 _:_ -> 519 channel_failure_msg(RemoteId) 520 end, 521 {[{connection_reply,Reply}], Connection}; 522 523handle_msg(#ssh_msg_channel_request{request_type = "subsystem"}, 524 Connection, client) -> 525 %% The client SHOULD ignore subsystem requests. See RFC 4254 6.5. 526 {[], Connection}; 527 528handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, 529 request_type = "pty-req", 530 want_reply = WantReply, 531 data = Data}, 532 Connection, server) -> 533 <<?DEC_BIN(BTermName,_TermLen), 534 ?UINT32(Width),?UINT32(Height), 535 ?UINT32(PixWidth), ?UINT32(PixHeight), 536 Modes/binary>> = Data, 537 TermName = binary_to_list(BTermName), 538 PtyRequest = {TermName, Width, Height, 539 PixWidth, PixHeight, decode_pty_opts(Modes)}, 540 handle_cli_msg(Connection, ChannelId, 541 {pty, ChannelId, WantReply, PtyRequest}); 542 543handle_msg(#ssh_msg_channel_request{request_type = "pty-req"}, 544 Connection, client) -> 545 %% The client SHOULD ignore pty requests. See RFC 4254 6.2. 546 {[], Connection}; 547 548handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, 549 request_type = "shell", 550 want_reply = WantReply}, 551 Connection, server) -> 552 handle_cli_msg(Connection, ChannelId, 553 {shell, ChannelId, WantReply}); 554 555handle_msg(#ssh_msg_channel_request{request_type = "shell"}, 556 Connection, client) -> 557 %% The client SHOULD ignore shell requests. See RFC 4254 6.5. 558 {[], Connection}; 559 560handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, 561 request_type = "exec", 562 want_reply = WantReply, 563 data = Data}, 564 Connection, server) -> 565 <<?DEC_BIN(Command, _Len)>> = Data, 566 handle_cli_msg(Connection, ChannelId, 567 {exec, ChannelId, WantReply, binary_to_list(Command)}); 568 569handle_msg(#ssh_msg_channel_request{request_type = "exec"}, 570 Connection, client) -> 571 %% The client SHOULD ignore exec requests. See RFC 4254 6.5. 572 {[], Connection}; 573 574handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, 575 request_type = "env", 576 want_reply = WantReply, 577 data = Data}, 578 Connection, server) -> 579 <<?DEC_BIN(Var,_VarLen), ?DEC_BIN(Value,_ValLen)>> = Data, 580 handle_cli_msg(Connection, ChannelId, 581 {env, ChannelId, WantReply, Var, Value}); 582 583handle_msg(#ssh_msg_channel_request{request_type = "env"}, 584 Connection, client) -> 585 %% The client SHOULD ignore env requests. 586 {[], Connection}; 587 588handle_msg(#ssh_msg_channel_request{recipient_channel = ChannelId, 589 request_type = _Other, 590 want_reply = WantReply}, 591 #connection{channel_cache = Cache} = Connection, _) -> 592 if WantReply == true -> 593 case ssh_client_channel:cache_lookup(Cache, ChannelId) of 594 #channel{remote_id = RemoteId} -> 595 FailMsg = channel_failure_msg(RemoteId), 596 {[{connection_reply, FailMsg}], Connection}; 597 undefined -> %% Chanel has been closed 598 {[], Connection} 599 end; 600 true -> 601 {[], Connection} 602 end; 603 604handle_msg(#ssh_msg_global_request{name = _Type, 605 want_reply = WantReply, 606 data = _Data}, Connection, _) -> 607 if WantReply == true -> 608 FailMsg = request_failure_msg(), 609 {[{connection_reply, FailMsg}], Connection}; 610 true -> 611 {[], Connection} 612 end; 613 614handle_msg(#ssh_msg_request_failure{}, 615 #connection{requests = [{_, From} | Rest]} = Connection, _) -> 616 {[{channel_request_reply, From, {failure, <<>>}}], 617 Connection#connection{requests = Rest}}; 618 619handle_msg(#ssh_msg_request_success{data = Data}, 620 #connection{requests = [{_, From} | Rest]} = Connection, _) -> 621 {[{channel_request_reply, From, {success, Data}}], 622 Connection#connection{requests = Rest}}; 623 624handle_msg(#ssh_msg_disconnect{code = Code, 625 description = Description}, 626 Connection, _) -> 627 {disconnect, {Code, Description}, handle_stop(Connection)}. 628 629 630%%%---------------------------------------------------------------- 631%%% Returns pending responses to be delivered to the peer when a 632%%% Channel/Connection closes 633%%% 634handle_stop(#connection{channel_cache = Cache} = Connection0) -> 635 {Connection, Replies} = 636 ssh_client_channel:cache_foldl( 637 fun(Channel, {Connection1, Acc}) -> 638 {Reply, Connection2} = 639 reply_msg(Channel, Connection1, 640 {closed, Channel#channel.local_id}), 641 {Connection2, Reply ++ Acc} 642 end, {Connection0, []}, Cache), 643 ssh_client_channel:cache_delete(Cache), 644 {Replies, Connection}. 645 646%%%---------------------------------------------------------------- 647%%% channel_*_msg(...) 648%%% Returns a #ssh_msg_....{} for channel operations. 649%%% 650channel_adjust_window_msg(ChannelId, Bytes) -> 651 #ssh_msg_channel_window_adjust{recipient_channel = ChannelId, 652 bytes_to_add = Bytes}. 653 654channel_close_msg(ChannelId) -> 655 #ssh_msg_channel_close {recipient_channel = ChannelId}. 656 657channel_data_msg(ChannelId, 0, Data) -> 658 #ssh_msg_channel_data{recipient_channel = ChannelId, 659 data = Data}; 660channel_data_msg(ChannelId, Type, Data) -> 661 #ssh_msg_channel_extended_data{recipient_channel = ChannelId, 662 data_type_code = Type, 663 data = Data}. 664 665channel_eof_msg(ChannelId) -> 666 #ssh_msg_channel_eof{recipient_channel = ChannelId}. 667 668channel_failure_msg(ChannelId) -> 669 #ssh_msg_channel_failure{recipient_channel = ChannelId}. 670 671channel_open_msg(Type, ChannelId, WindowSize, MaxPacketSize, Data) -> 672 #ssh_msg_channel_open{channel_type = Type, 673 sender_channel = ChannelId, 674 initial_window_size = WindowSize, 675 maximum_packet_size = MaxPacketSize, 676 data = Data 677 }. 678 679channel_open_confirmation_msg(RemoteId, LID, WindowSize, PacketSize) -> 680 #ssh_msg_channel_open_confirmation{recipient_channel = RemoteId, 681 sender_channel = LID, 682 initial_window_size = WindowSize, 683 maximum_packet_size = PacketSize}. 684 685channel_open_failure_msg(RemoteId, Reason, Description, Lang) -> 686 #ssh_msg_channel_open_failure{recipient_channel = RemoteId, 687 reason = Reason, 688 description = Description, 689 lang = Lang}. 690 691channel_status_msg({success, ChannelId}) -> 692 channel_success_msg(ChannelId); 693 694channel_status_msg({failure, ChannelId}) -> 695 channel_failure_msg(ChannelId). 696 697channel_request_msg(ChannelId, Type, WantReply, Data) -> 698 #ssh_msg_channel_request{recipient_channel = ChannelId, 699 request_type = Type, 700 want_reply = WantReply, 701 data = Data}. 702 703channel_success_msg(ChannelId) -> 704 #ssh_msg_channel_success{recipient_channel = ChannelId}. 705 706%%%---------------------------------------------------------------- 707%%% request_*_msg(...) 708%%% Returns a #ssh_msg_....{} for request responses. 709%%% 710request_failure_msg() -> 711 #ssh_msg_request_failure{}. 712 713request_success_msg(Data) -> 714 #ssh_msg_request_success{data = Data}. 715 716%%%---------------------------------------------------------------- 717%%% 718%%% 719bind(IP, Port, ChannelPid, Connection) -> 720 Binds = [{{IP, Port}, ChannelPid} 721 | lists:keydelete({IP, Port}, 1, 722 Connection#connection.port_bindings)], 723 Connection#connection{port_bindings = Binds}. 724 725unbind(IP, Port, Connection) -> 726 Connection#connection{ 727 port_bindings = 728 lists:keydelete({IP, Port}, 1, 729 Connection#connection.port_bindings)}. 730unbind_channel(ChannelPid, Connection) -> 731 Binds = [{Bind, ChannelP} || {Bind, ChannelP} 732 <- Connection#connection.port_bindings, 733 ChannelP =/= ChannelPid], 734 Connection#connection{port_bindings = Binds}. 735 736bound_channel(IP, Port, Connection) -> 737 case lists:keysearch({IP, Port}, 1, Connection#connection.port_bindings) of 738 {value, {{IP, Port}, ChannelPid}} -> ChannelPid; 739 _ -> undefined 740 end. 741 742encode_ip(Addr) when is_tuple(Addr) -> 743 case catch inet_parse:ntoa(Addr) of 744 {'EXIT',_} -> false; 745 A -> A 746 end; 747encode_ip(Addr) when is_list(Addr) -> 748 case inet_parse:address(Addr) of 749 {ok, _} -> Addr; 750 Error -> 751 case inet:getaddr(Addr, inet) of 752 {ok, A} -> 753 inet_parse:ntoa(A); 754 Error -> false 755 end 756 end. 757 758%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 759%%% 760%%% Internal functions 761%%% 762%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 763 764%%%---------------------------------------------------------------- 765%%% Create the channel data when an ssh_msg_open_channel message 766%%% of "session" typ is handled 767%%% 768setup_session(#connection{channel_cache = Cache, 769 channel_id_seed = NewChannelID 770 } = C, 771 RemoteId, Type, WindowSize, PacketSize) -> 772 NextChannelID = NewChannelID + 1, 773 Channel = 774 #channel{type = Type, 775 sys = "ssh", 776 local_id = NewChannelID, 777 recv_window_size = ?DEFAULT_WINDOW_SIZE, 778 recv_packet_size = ?DEFAULT_PACKET_SIZE, 779 send_window_size = WindowSize, 780 send_packet_size = PacketSize, 781 send_buf = queue:new(), 782 remote_id = RemoteId 783 }, 784 ssh_client_channel:cache_update(Cache, Channel), 785 OpenConfMsg = channel_open_confirmation_msg(RemoteId, NewChannelID, 786 ?DEFAULT_WINDOW_SIZE, 787 ?DEFAULT_PACKET_SIZE), 788 Reply = {connection_reply, OpenConfMsg}, 789 {[Reply], C#connection{channel_id_seed = NextChannelID}}. 790 791 792%%%---------------------------------------------------------------- 793%%% Start a cli or subsystem 794%%% 795start_cli(#connection{options = Options, 796 cli_spec = CliSpec, 797 exec = Exec, 798 sub_system_supervisor = SubSysSup}, ChannelId) -> 799 case CliSpec of 800 no_cli -> 801 {error, cli_disabled}; 802 {CbModule, Args} -> 803 start_channel(CbModule, ChannelId, Args, SubSysSup, Exec, Options) 804 end. 805 806 807start_subsystem(BinName, #connection{options = Options, 808 sub_system_supervisor = SubSysSup}, 809 #channel{local_id = ChannelId}, _ReplyMsg) -> 810 Name = binary_to_list(BinName), 811 case check_subsystem(Name, Options) of 812 {Callback, Opts} when is_atom(Callback), Callback =/= none -> 813 start_channel(Callback, ChannelId, Opts, SubSysSup, Options); 814 {Other, _} when Other =/= none -> 815 {error, legacy_option_not_supported} 816 end. 817 818 819%%% Helpers for starting cli/subsystems 820start_channel(Cb, Id, Args, SubSysSup, Opts) -> 821 start_channel(Cb, Id, Args, SubSysSup, undefined, Opts). 822 823start_channel(Cb, Id, Args, SubSysSup, Exec, Opts) -> 824 ChannelSup = ssh_subsystem_sup:channel_supervisor(SubSysSup), 825 case max_num_channels_not_exceeded(ChannelSup, Opts) of 826 true -> 827 case ssh_server_channel_sup:start_child(ChannelSup, Cb, Id, Args, Exec) of 828 {error,{Error,_Info}} -> 829 throw(Error); 830 Others -> 831 Others 832 end; 833 false -> 834 throw(max_num_channels_exceeded) 835 end. 836 837max_num_channels_not_exceeded(ChannelSup, Opts) -> 838 MaxNumChannels = ?GET_OPT(max_channels, Opts), 839 NumChannels = length([x || {_,_,worker,[ssh_server_channel]} <- 840 supervisor:which_children(ChannelSup)]), 841 %% Note that NumChannels is BEFORE starting a new one 842 NumChannels < MaxNumChannels. 843 844check_subsystem("sftp"= SsName, Options) -> 845 case ?GET_OPT(subsystems, Options) of 846 no_subsys -> % FIXME: Can 'no_subsys' ever be matched? 847 {SsName, {Cb, Opts}} = ssh_sftpd:subsystem_spec([]), 848 {Cb, Opts}; 849 SubSystems -> 850 proplists:get_value(SsName, SubSystems, {none, []}) 851 end; 852 853check_subsystem(SsName, Options) -> 854 Subsystems = ?GET_OPT(subsystems, Options), 855 case proplists:get_value(SsName, Subsystems, {none, []}) of 856 Fun when is_function(Fun) -> 857 {Fun, []}; 858 {_, _} = Value -> 859 Value 860 end. 861 862%%%---------------------------------------------------------------- 863%%% 864%%% Send-window handling 865%%% 866 867update_send_window(Channel, _, undefined, 868 #connection{channel_cache = Cache}) -> 869 do_update_send_window(Channel, Cache); 870 871update_send_window(#channel{send_buf = SendBuffer} = Channel, DataType, Data, 872 #connection{channel_cache = Cache}) -> 873 do_update_send_window(Channel#channel{send_buf = queue:in({DataType, Data}, SendBuffer)}, 874 Cache). 875 876do_update_send_window(Channel0, Cache) -> 877 {SendMsgs, Channel} = get_window(Channel0, []), 878 ssh_client_channel:cache_update(Cache, Channel), 879 {SendMsgs, Channel}. 880 881get_window(#channel{send_window_size = 0 882 } = Channel, Acc) -> 883 {lists:reverse(Acc), Channel}; 884get_window(#channel{send_packet_size = 0 885 } = Channel, Acc) -> 886 {lists:reverse(Acc), Channel}; 887get_window(#channel{send_buf = Buffer, 888 send_packet_size = PacketSize, 889 send_window_size = WindowSize0 890 } = Channel, Acc0) -> 891 case queue:out(Buffer) of 892 {{value, {_, Data} = Msg}, NewBuffer} -> 893 case handle_send_window(Msg, size(Data), PacketSize, WindowSize0, Acc0) of 894 {WindowSize, Acc, {_, <<>>}} -> 895 {lists:reverse(Acc), Channel#channel{send_window_size = WindowSize, 896 send_buf = NewBuffer}}; 897 {WindowSize, Acc, Rest} -> 898 get_window(Channel#channel{send_window_size = WindowSize, 899 send_buf = queue:in_r(Rest, NewBuffer)}, Acc) 900 end; 901 {empty, NewBuffer} -> 902 {[], Channel#channel{send_buf = NewBuffer}} 903 end. 904 905handle_send_window(Msg = {Type, Data}, Size, PacketSize, WindowSize, Acc) when Size =< WindowSize -> 906 case Size =< PacketSize of 907 true -> 908 {WindowSize - Size, [Msg | Acc], {Type, <<>>}}; 909 false -> 910 <<Msg1:PacketSize/binary, Msg2/binary>> = Data, 911 {WindowSize - PacketSize, [{Type, Msg1} | Acc], {Type, Msg2}} 912 end; 913handle_send_window({Type, Data}, _, PacketSize, WindowSize, Acc) when WindowSize =< PacketSize -> 914 <<Msg1:WindowSize/binary, Msg2/binary>> = Data, 915 {WindowSize - WindowSize, [{Type, Msg1} | Acc], {Type, Msg2}}; 916handle_send_window({Type, Data}, _, PacketSize, WindowSize, Acc) -> 917 <<Msg1:PacketSize/binary, Msg2/binary>> = Data, 918 {WindowSize - PacketSize, [{Type, Msg1} | Acc], {Type, Msg2}}. 919 920%%%---------------------------------------------------------------- 921%%% 922%%% Flow control 923%%% 924 925flow_control(Channel, Cache) -> 926 flow_control([window_adjusted], Channel, Cache). 927 928flow_control([], Channel, Cache) -> 929 ssh_client_channel:cache_update(Cache, Channel), 930 []; 931flow_control([_|_], #channel{flow_control = From, 932 send_buf = Buffer} = Channel, Cache) when From =/= undefined -> 933 case queue:is_empty(Buffer) of 934 true -> 935 ssh_client_channel:cache_update(Cache, Channel#channel{flow_control = undefined}), 936 [{flow_control, Cache, Channel, From, ok}]; 937 false -> 938 [] 939 end; 940flow_control(_,_,_) -> 941 []. 942 943%%%---------------------------------------------------------------- 944%%% 945%%% Pseudo terminal stuff 946%%% 947 948pty_req(ConnectionHandler, Channel, Term, Width, Height, 949 PixWidth, PixHeight, PtyOpts, TimeOut) -> 950 ssh_connection_handler:request(ConnectionHandler, 951 Channel, "pty-req", true, 952 [?string(Term), 953 ?uint32(Width), ?uint32(Height), 954 ?uint32(PixWidth),?uint32(PixHeight), 955 encode_pty_opts(PtyOpts)], TimeOut). 956 957pty_default_dimensions(Dimension, TermData) -> 958 case proplists:get_value(Dimension, TermData, 0) of 959 N when is_integer(N), N > 0 -> 960 {N, 0}; 961 _ -> 962 PixelDim = list_to_atom("pixel_" ++ atom_to_list(Dimension)), 963 case proplists:get_value(PixelDim, TermData, 0) of 964 N when is_integer(N), N > 0 -> 965 {0, N}; 966 _ -> 967 {?TERMINAL_WIDTH, 0} 968 end 969 end. 970 971encode_pty_opts(Opts) -> 972 Bin = list_to_binary(encode_pty_opts2(Opts)), 973 <<?STRING(Bin)>>. 974 975encode_pty_opts2([]) -> 976 [?TTY_OP_END]; 977encode_pty_opts2([{vintr,Value} | Opts]) -> 978 [?VINTR, ?uint32(Value) | encode_pty_opts2(Opts)]; 979encode_pty_opts2([{vquit,Value} | Opts]) -> 980 [?VQUIT, ?uint32(Value) | encode_pty_opts2(Opts)]; 981encode_pty_opts2([{verase,Value} | Opts]) -> 982 [?VERASE, ?uint32(Value) | encode_pty_opts2(Opts)]; 983encode_pty_opts2([{vkill,Value} | Opts]) -> 984 [?VKILL, ?uint32(Value) | encode_pty_opts2(Opts)]; 985encode_pty_opts2([{veof,Value} | Opts]) -> 986 [?VEOF, ?uint32(Value) | encode_pty_opts2(Opts)]; 987encode_pty_opts2([{veol,Value} | Opts]) -> 988 [?VEOL, ?uint32(Value) | encode_pty_opts2(Opts)]; 989encode_pty_opts2([{veol2,Value} | Opts]) -> 990 [?VEOL2, ?uint32(Value) | encode_pty_opts2(Opts)]; 991encode_pty_opts2([{vstart,Value} | Opts]) -> 992 [?VSTART, ?uint32(Value) | encode_pty_opts2(Opts)]; 993encode_pty_opts2([{vstop,Value} | Opts]) -> 994 [?VSTOP, ?uint32(Value) | encode_pty_opts2(Opts)]; 995encode_pty_opts2([{vsusp,Value} | Opts]) -> 996 [?VSUSP, ?uint32(Value) | encode_pty_opts2(Opts)]; 997encode_pty_opts2([{vdsusp,Value} | Opts]) -> 998 [?VDSUSP, ?uint32(Value) | encode_pty_opts2(Opts)]; 999encode_pty_opts2([{vreprint,Value} | Opts]) -> 1000 [?VREPRINT, ?uint32(Value) | encode_pty_opts2(Opts)]; 1001encode_pty_opts2([{vwerase,Value} | Opts]) -> 1002 [ ?VWERASE, ?uint32(Value) | encode_pty_opts2(Opts)]; 1003encode_pty_opts2([{vlnext,Value} | Opts]) -> 1004 [?VLNEXT, ?uint32(Value) | encode_pty_opts2(Opts)]; 1005encode_pty_opts2([{vflush,Value} | Opts]) -> 1006 [?VFLUSH, ?uint32(Value) | encode_pty_opts2(Opts)]; 1007encode_pty_opts2([{vswtch,Value} | Opts]) -> 1008 [?VSWTCH, ?uint32(Value) | encode_pty_opts2(Opts)]; 1009encode_pty_opts2([{vstatus,Value} | Opts]) -> 1010 [?VSTATUS, ?uint32(Value) | encode_pty_opts2(Opts)]; 1011encode_pty_opts2([{vdiscard,Value} | Opts]) -> 1012 [?VDISCARD, ?uint32(Value) | encode_pty_opts2(Opts)]; 1013encode_pty_opts2([{ignpar,Value} | Opts]) -> 1014 [?IGNPAR, ?uint32(Value) | encode_pty_opts2(Opts)]; 1015encode_pty_opts2([{parmrk,Value} | Opts]) -> 1016 [?PARMRK, ?uint32(Value) | encode_pty_opts2(Opts)]; 1017encode_pty_opts2([{inpck,Value} | Opts]) -> 1018 [?INPCK, ?uint32(Value) | encode_pty_opts2(Opts)]; 1019encode_pty_opts2([{istrip,Value} | Opts]) -> 1020 [?ISTRIP, ?uint32(Value) | encode_pty_opts2(Opts)]; 1021encode_pty_opts2([{inlcr,Value} | Opts]) -> 1022 [?INLCR, ?uint32(Value) | encode_pty_opts2(Opts)]; 1023encode_pty_opts2([{igncr,Value} | Opts]) -> 1024 [?IGNCR, ?uint32(Value) | encode_pty_opts2(Opts)]; 1025encode_pty_opts2([{icrnl,Value} | Opts]) -> 1026 [?ICRNL, ?uint32(Value) | encode_pty_opts2(Opts)]; 1027encode_pty_opts2([{iuclc,Value} | Opts]) -> 1028 [?IUCLC, ?uint32(Value) | encode_pty_opts2(Opts)]; 1029encode_pty_opts2([{ixon,Value} | Opts]) -> 1030 [?IXON, ?uint32(Value) | encode_pty_opts2(Opts)]; 1031encode_pty_opts2([{ixany,Value} | Opts]) -> 1032 [?IXANY, ?uint32(Value) | encode_pty_opts2(Opts)]; 1033encode_pty_opts2([{ixoff,Value} | Opts]) -> 1034 [?IXOFF, ?uint32(Value) | encode_pty_opts2(Opts)]; 1035encode_pty_opts2([{imaxbel,Value} | Opts]) -> 1036 [?IMAXBEL, ?uint32(Value) | encode_pty_opts2(Opts)]; 1037encode_pty_opts2([{isig,Value} | Opts]) -> 1038 [?ISIG, ?uint32(Value) | encode_pty_opts2(Opts)]; 1039encode_pty_opts2([{icanon,Value} | Opts]) -> 1040 [?ICANON, ?uint32(Value) | encode_pty_opts2(Opts)]; 1041encode_pty_opts2([{xcase,Value} | Opts]) -> 1042 [?XCASE, ?uint32(Value) | encode_pty_opts2(Opts)]; 1043encode_pty_opts2([{echo,Value} | Opts]) -> 1044 [?ECHO, ?uint32(Value) | encode_pty_opts2(Opts)]; 1045encode_pty_opts2([{echoe,Value} | Opts]) -> 1046 [?ECHOE, ?uint32(Value) | encode_pty_opts2(Opts)]; 1047encode_pty_opts2([{echok,Value} | Opts]) -> 1048 [?ECHOK, ?uint32(Value) | encode_pty_opts2(Opts)]; 1049encode_pty_opts2([{echonl,Value} | Opts]) -> 1050 [?ECHONL, ?uint32(Value) | encode_pty_opts2(Opts)]; 1051encode_pty_opts2([{noflsh,Value} | Opts]) -> 1052 [?NOFLSH, ?uint32(Value) | encode_pty_opts2(Opts)]; 1053encode_pty_opts2([{tostop,Value} | Opts]) -> 1054 [?TOSTOP, ?uint32(Value) | encode_pty_opts2(Opts)]; 1055encode_pty_opts2([{iexten,Value} | Opts]) -> 1056 [?IEXTEN, ?uint32(Value) | encode_pty_opts2(Opts)]; 1057encode_pty_opts2([{echoctl,Value} | Opts]) -> 1058 [?ECHOCTL, ?uint32(Value) | encode_pty_opts2(Opts)]; 1059encode_pty_opts2([{echoke,Value} | Opts]) -> 1060 [?ECHOKE, ?uint32(Value) | encode_pty_opts2(Opts)]; 1061encode_pty_opts2([{pendin,Value} | Opts]) -> 1062 [?PENDIN, ?uint32(Value) | encode_pty_opts2(Opts)]; 1063encode_pty_opts2([{opost,Value} | Opts]) -> 1064 [?OPOST, ?uint32(Value) | encode_pty_opts2(Opts)]; 1065encode_pty_opts2([{olcuc,Value} | Opts]) -> 1066 [?OLCUC, ?uint32(Value) | encode_pty_opts2(Opts)]; 1067encode_pty_opts2([{onlcr,Value} | Opts]) -> 1068 [?ONLCR, ?uint32(Value) | encode_pty_opts2(Opts)]; 1069encode_pty_opts2([{ocrnl,Value} | Opts]) -> 1070 [?OCRNL, ?uint32(Value) | encode_pty_opts2(Opts)]; 1071encode_pty_opts2([{onocr,Value} | Opts]) -> 1072 [?ONOCR, ?uint32(Value) | encode_pty_opts2(Opts)]; 1073encode_pty_opts2([{onlret,Value} | Opts]) -> 1074 [?ONLRET, ?uint32(Value) | encode_pty_opts2(Opts)]; 1075encode_pty_opts2([{cs7,Value} | Opts]) -> 1076 [?CS7, ?uint32(Value) | encode_pty_opts2(Opts)]; 1077encode_pty_opts2([{cs8,Value} | Opts]) -> 1078 [?CS8, ?uint32(Value) | encode_pty_opts2(Opts)]; 1079encode_pty_opts2([{parenb,Value} | Opts]) -> 1080 [?PARENB, ?uint32(Value) | encode_pty_opts2(Opts)]; 1081encode_pty_opts2([{parodd,Value} | Opts]) -> 1082 [?PARODD, ?uint32(Value) | encode_pty_opts2(Opts)]; 1083encode_pty_opts2([{tty_op_ispeed,Value} | Opts]) -> 1084 [?TTY_OP_ISPEED, ?uint32(Value) | encode_pty_opts2(Opts)]; 1085encode_pty_opts2([{tty_op_ospeed,Value} | Opts]) -> 1086 [?TTY_OP_OSPEED, ?uint32(Value) | encode_pty_opts2(Opts)]. 1087 1088decode_pty_opts(<<>>) -> 1089 []; 1090decode_pty_opts(<<0, 0, 0, 0>>) -> 1091 []; 1092decode_pty_opts(<<?DEC_BIN(Modes,_Len)>>) -> 1093 decode_pty_opts2(Modes); 1094decode_pty_opts(Binary) -> 1095 decode_pty_opts2(Binary). 1096 1097decode_pty_opts2(<<?TTY_OP_END>>) -> 1098 []; 1099decode_pty_opts2(<<Code, ?UINT32(Value), Tail/binary>>) -> 1100 Op = case Code of 1101 ?VINTR -> vintr; 1102 ?VQUIT -> vquit; 1103 ?VERASE -> verase; 1104 ?VKILL -> vkill; 1105 ?VEOF -> veof; 1106 ?VEOL -> veol; 1107 ?VEOL2 -> veol2; 1108 ?VSTART -> vstart; 1109 ?VSTOP -> vstop; 1110 ?VSUSP -> vsusp; 1111 ?VDSUSP -> vdsusp; 1112 ?VREPRINT -> vreprint; 1113 ?VWERASE -> vwerase; 1114 ?VLNEXT -> vlnext; 1115 ?VFLUSH -> vflush; 1116 ?VSWTCH -> vswtch; 1117 ?VSTATUS -> vstatus; 1118 ?VDISCARD -> vdiscard; 1119 ?IGNPAR -> ignpar; 1120 ?PARMRK -> parmrk; 1121 ?INPCK -> inpck; 1122 ?ISTRIP -> istrip; 1123 ?INLCR -> inlcr; 1124 ?IGNCR -> igncr; 1125 ?ICRNL -> icrnl; 1126 ?IUCLC -> iuclc; 1127 ?IXON -> ixon; 1128 ?IXANY -> ixany; 1129 ?IXOFF -> ixoff; 1130 ?IMAXBEL -> imaxbel; 1131 ?ISIG -> isig; 1132 ?ICANON -> icanon; 1133 ?XCASE -> xcase; 1134 ?ECHO -> echo; 1135 ?ECHOE -> echoe; 1136 ?ECHOK -> echok; 1137 ?ECHONL -> echonl; 1138 ?NOFLSH -> noflsh; 1139 ?TOSTOP -> tostop; 1140 ?IEXTEN -> iexten; 1141 ?ECHOCTL -> echoctl; 1142 ?ECHOKE -> echoke; 1143 ?PENDIN -> pendin; 1144 ?OPOST -> opost; 1145 ?OLCUC -> olcuc; 1146 ?ONLCR -> onlcr; 1147 ?OCRNL -> ocrnl; 1148 ?ONOCR -> onocr; 1149 ?ONLRET -> onlret; 1150 ?CS7 -> cs7; 1151 ?CS8 -> cs8; 1152 ?PARENB -> parenb; 1153 ?PARODD -> parodd; 1154 ?TTY_OP_ISPEED -> tty_op_ispeed; 1155 ?TTY_OP_OSPEED -> tty_op_ospeed; 1156 _ -> Code 1157 end, 1158 [{Op, Value} | decode_pty_opts2(Tail)]. 1159 1160 1161backwards_compatible([], Acc) -> 1162 Acc; 1163backwards_compatible([{hight, Value} | Rest], Acc) -> 1164 backwards_compatible(Rest, [{height, Value} | Acc]); 1165backwards_compatible([{pixel_hight, Value} | Rest], Acc) -> 1166 backwards_compatible(Rest, [{height, Value} | Acc]); 1167backwards_compatible([Value| Rest], Acc) -> 1168 backwards_compatible(Rest, [ Value | Acc]). 1169 1170 1171%%%---------------------------------------------------------------- 1172%%% 1173%%% Common part of handling channel messages meant for a cli (like "env", "exec" etc) 1174%%% Called at the finnish of handle_msg(#ssh_msg_channel_request,...) 1175%%% 1176 1177handle_cli_msg(C0, ChId, Reply0) -> 1178 Cache = C0#connection.channel_cache, 1179 Ch0 = ssh_client_channel:cache_lookup(Cache, ChId), 1180 case Ch0#channel.user of 1181 undefined -> 1182 case (catch start_cli(C0, ChId)) of 1183 {ok, Pid} -> 1184 erlang:monitor(process, Pid), 1185 Ch = Ch0#channel{user = Pid}, 1186 ssh_client_channel:cache_update(Cache, Ch), 1187 reply_msg(Ch, C0, Reply0); 1188 _Other -> 1189 Reply = {connection_reply, channel_failure_msg(Ch0#channel.remote_id)}, 1190 {[Reply], C0} 1191 end; 1192 1193 _ -> 1194 reply_msg(Ch0, C0, Reply0) 1195 end. 1196 1197%%%---------------------------------------------------------------- 1198%%% 1199%%% Request response handling on return to the calling ssh_connection_handler 1200%%% state machine. 1201%%% 1202 1203channel_data_reply_msg(ChannelId, Connection, DataType, Data) -> 1204 case ssh_client_channel:cache_lookup(Connection#connection.channel_cache, ChannelId) of 1205 #channel{recv_window_size = Size} = Channel -> 1206 WantedSize = Size - size(Data), 1207 ssh_client_channel:cache_update(Connection#connection.channel_cache, 1208 Channel#channel{recv_window_size = WantedSize}), 1209 reply_msg(Channel, Connection, {data, ChannelId, DataType, Data}); 1210 undefined -> 1211 {[], Connection} 1212 end. 1213 1214 1215reply_msg(ChId, C, Reply) when is_integer(ChId) -> 1216 reply_msg(ssh_client_channel:cache_lookup(C#connection.channel_cache, ChId), C, Reply); 1217 1218reply_msg(Channel, Connection, {open, _} = Reply) -> 1219 request_reply_or_data(Channel, Connection, Reply); 1220reply_msg(Channel, Connection, {open_error, _, _, _} = Reply) -> 1221 request_reply_or_data(Channel, Connection, Reply); 1222reply_msg(Channel, Connection, success = Reply) -> 1223 request_reply_or_data(Channel, Connection, Reply); 1224reply_msg(Channel, Connection, failure = Reply) -> 1225 request_reply_or_data(Channel, Connection, Reply); 1226reply_msg(Channel, Connection, {closed, _} = Reply) -> 1227 request_reply_or_data(Channel, Connection, Reply); 1228reply_msg(undefined, Connection, _Reply) -> 1229 {[], Connection}; 1230reply_msg(#channel{user = ChannelPid}, Connection, Reply) -> 1231 {[{channel_data, ChannelPid, Reply}], Connection}. 1232 1233 1234request_reply_or_data(#channel{local_id = ChannelId, user = ChannelPid}, 1235 #connection{requests = Requests} = 1236 Connection, Reply) -> 1237 case lists:keysearch(ChannelId, 1, Requests) of 1238 {value, {ChannelId, From}} -> 1239 {[{channel_request_reply, From, Reply}], 1240 Connection#connection{requests = 1241 lists:keydelete(ChannelId, 1, Requests)}}; 1242 false when (Reply == success) or (Reply == failure) -> 1243 {[], Connection}; 1244 false -> 1245 {[{channel_data, ChannelPid, Reply}], Connection} 1246 end. 1247