ssh/session/
session_local.rs1use std::{
2 cell::RefCell,
3 io::{Read, Write},
4 rc::Rc,
5 time::Duration,
6};
7use tracing::*;
8
9#[cfg(feature = "scp")]
10use crate::channel::LocalScp;
11use crate::{
12 channel::{LocalChannel, LocalExec, LocalShell},
13 client::Client,
14 constant::{size, ssh_channel_fail_code, ssh_connection_code, ssh_str},
15 error::{SshError, SshResult},
16 model::TerminalSize,
17 model::{Data, Packet, RcMut, SecPacket, U32Iter},
18};
19
20pub struct LocalSession<S>
21where
22 S: Read + Write,
23{
24 client: RcMut<Client>,
25 stream: RcMut<S>,
26 channel_num: U32Iter,
27}
28
29impl<S> LocalSession<S>
30where
31 S: Read + Write,
32{
33 pub(crate) fn new(client: Client, stream: S) -> Self {
34 Self {
35 client: Rc::new(RefCell::new(client)),
36 stream: Rc::new(RefCell::new(stream)),
37 channel_num: U32Iter::default(),
38 }
39 }
40
41 pub fn close(self) {
44 info!("Client close");
45 drop(self)
46 }
47
48 pub fn set_timeout(&mut self, timeout: Option<Duration>) {
52 self.client.borrow_mut().set_timeout(timeout)
53 }
54
55 pub fn open_exec(&mut self) -> SshResult<LocalExec<S>> {
58 let channel = self.open_channel()?;
59 channel.exec()
60 }
61
62 #[cfg(feature = "scp")]
65 pub fn open_scp(&mut self) -> SshResult<LocalScp<S>> {
66 let channel = self.open_channel()?;
67 channel.scp()
68 }
69
70 pub fn open_shell(&mut self) -> SshResult<LocalShell<S>> {
73 self.open_shell_terminal(TerminalSize::from(80, 24))
74 }
75
76 pub fn open_shell_terminal(&mut self, tv: TerminalSize) -> SshResult<LocalShell<S>> {
81 let channel = self.open_channel()?;
82 channel.shell(tv)
83 }
84
85 pub fn get_raw_io(&mut self) -> RcMut<S> {
86 self.stream.clone()
87 }
88
89 pub fn open_channel(&mut self) -> SshResult<LocalChannel<S>> {
94 info!("channel opened.");
95
96 let client_channel_no = self.channel_num.next().unwrap();
97 self.send_open_channel(client_channel_no)?;
98 let (server_channel_no, remote_window_size) = self.receive_open_channel()?;
99
100 Ok(LocalChannel::new(
101 server_channel_no,
102 client_channel_no,
103 remote_window_size,
104 self.client.clone(),
105 self.stream.clone(),
106 ))
107 }
108
109 fn send_open_channel(&mut self, client_channel_no: u32) -> SshResult<()> {
111 let mut data = Data::new();
112 data.put_u8(ssh_connection_code::CHANNEL_OPEN)
113 .put_str(ssh_str::SESSION)
114 .put_u32(client_channel_no)
115 .put_u32(size::LOCAL_WINDOW_SIZE)
116 .put_u32(size::BUF_SIZE as u32);
117 data.pack(&mut self.client.borrow_mut())
118 .write_stream(&mut *self.stream.borrow_mut())
119 }
120
121 fn receive_open_channel(&mut self) -> SshResult<(u32, u32)> {
123 loop {
124 let mut data = Data::unpack(SecPacket::from_stream(
125 &mut *self.stream.borrow_mut(),
126 &mut self.client.borrow_mut(),
127 )?)?;
128
129 let message_code = data.get_u8();
130 match message_code {
131 ssh_connection_code::CHANNEL_OPEN_CONFIRMATION => {
133 data.get_u32();
134 let server_channel_no = data.get_u32();
135 let remote_window_size = data.get_u32();
136 data.get_u32();
138 return Ok((server_channel_no, remote_window_size));
139 }
140 ssh_connection_code::CHANNEL_OPEN_FAILURE => {
149 data.get_u32();
150 let code = data.get_u32();
152 let description =
154 String::from_utf8(data.get_u8s()).unwrap_or_else(|_| String::from("error"));
155 data.get_u8s();
157
158 let err_msg = match code {
159 ssh_channel_fail_code::ADMINISTRATIVELY_PROHIBITED => {
160 format!("ADMINISTRATIVELY_PROHIBITED: {}", description)
161 }
162 ssh_channel_fail_code::CONNECT_FAILED => {
163 format!("CONNECT_FAILED: {}", description)
164 }
165 ssh_channel_fail_code::UNKNOWN_CHANNEL_TYPE => {
166 format!("UNKNOWN_CHANNEL_TYPE: {}", description)
167 }
168 ssh_channel_fail_code::RESOURCE_SHORTAGE => {
169 format!("RESOURCE_SHORTAGE: {}", description)
170 }
171 _ => description,
172 };
173 return Err(SshError::GeneralError(err_msg));
174 }
175 ssh_connection_code::GLOBAL_REQUEST => {
176 let mut data = Data::new();
177 data.put_u8(ssh_connection_code::REQUEST_FAILURE);
178 data.pack(&mut self.client.borrow_mut())
179 .write_stream(&mut *self.stream.borrow_mut())?;
180 continue;
181 }
182 x => {
183 debug!("Ignore ssh msg {}", x);
184 continue;
185 }
186 }
187 }
188 }
189}