1use crate::{
2 agent::Agent, channel::Channel, listener::Listener, sftp::Sftp, util::run_ssh2_fn, Error,
3};
4use smol::Async;
5use ssh2::{
6 self, DisconnectCode, HashType, HostKeyType, KeyboardInteractivePrompt, KnownHosts, MethodType,
7 ScpFileStat,
8};
9#[cfg(unix)]
10use std::os::unix::io::{AsRawFd, RawFd};
11#[cfg(windows)]
12use std::os::windows::io::{AsRawSocket, RawSocket};
13use std::{
14 convert::From,
15 net::TcpStream,
16 path::Path,
17 sync::{Arc, Once},
18};
19
20pub struct Session {
22 inner: ssh2::Session,
23 stream: Option<Arc<Async<TcpStream>>>,
24}
25
26#[cfg(unix)]
27struct RawFdWrapper(RawFd);
28
29#[cfg(unix)]
30impl AsRawFd for RawFdWrapper {
31 fn as_raw_fd(&self) -> RawFd {
32 self.0
33 }
34}
35
36#[cfg(windows)]
37struct RawSocketWrapper(RawSocket);
38
39#[cfg(windows)]
40impl AsRawSocket for RawSocketWrapper {
41 fn as_raw_socket(&self) -> RawSocket {
42 self.0
43 }
44}
45
46impl Session {
47 pub fn new() -> Result<Session, Error> {
49 let session = ssh2::Session::new()?;
50 static START: Once = Once::new();
51 START.call_once(|| {
52 std::thread::spawn(|| smol::run(futures::future::pending::<()>()));
53 });
54 session.set_blocking(false);
55
56 Ok(Self {
57 inner: session,
58 stream: None,
59 })
60 }
61
62 pub async fn set_banner(&self, banner: &str) -> Result<(), Error> {
64 run_ssh2_fn(self.stream.as_ref().unwrap(), || {
65 self.inner.set_banner(banner)
66 })
67 .await
68 }
69
70 pub fn set_allow_sigpipe(&self, block: bool) {
72 self.inner.set_allow_sigpipe(block)
73 }
74
75 pub fn set_compress(&self, compress: bool) {
77 self.inner.set_compress(compress)
78 }
79
80 pub fn is_blocking(&self) -> bool {
82 self.inner.is_blocking()
83 }
84
85 pub fn set_timeout(&self, timeout_ms: u32) {
87 self.inner.set_timeout(timeout_ms)
88 }
89
90 pub fn timeout(&self) -> u32 {
92 self.inner.timeout()
93 }
94
95 pub async fn handshake(&mut self) -> Result<(), Error> {
97 run_ssh2_fn(self.stream.as_ref().unwrap(), || {
98 self.inner.clone().handshake()
99 })
100 .await
101 }
102
103 pub fn set_tcp_stream(&mut self, stream: Async<TcpStream>) -> Result<(), Error> {
118 #[cfg(unix)]
119 {
120 let raw_fd = RawFdWrapper(stream.as_raw_fd());
121 self.inner.set_tcp_stream(raw_fd);
122 }
123 #[cfg(windows)]
124 {
125 let raw_socket = RawSocketWrapper(stream.as_raw_socket());
126 self.inner.set_tcp_stream(raw_socket);
127 }
128 self.stream = Some(Arc::new(stream));
129 Ok(())
130 }
131
132 pub async fn userauth_password(&self, username: &str, password: &str) -> Result<(), Error> {
134 run_ssh2_fn(self.stream.as_ref().unwrap(), || {
135 self.inner.userauth_password(username, password)
136 })
137 .await
138 }
139
140 pub fn userauth_keyboard_interactive<P: KeyboardInteractivePrompt>(
142 &self,
143 _username: &str,
144 _prompter: &mut P,
145 ) -> Result<(), Error> {
146 unimplemented!();
147 }
148
149 pub async fn userauth_agent(&self, username: &str) -> Result<(), Error> {
151 let mut agent = self.agent()?;
152 agent.connect().await?;
153 agent.list_identities()?;
154 let identities = agent.identities()?;
155 let identity = match identities.get(0) {
156 Some(identity) => identity,
157 None => return Err(Error::from(ssh2::Error::from_errno(-4))),
158 };
159 agent.userauth(username, &identity).await
160 }
161
162 pub async fn userauth_pubkey_file(
164 &self,
165 username: &str,
166 pubkey: Option<&Path>,
167 privatekey: &Path,
168 passphrase: Option<&str>,
169 ) -> Result<(), Error> {
170 run_ssh2_fn(self.stream.as_ref().unwrap(), || {
171 self.inner
172 .userauth_pubkey_file(username, pubkey, privatekey, passphrase)
173 })
174 .await
175 }
176
177 #[cfg(unix)]
179 pub async fn userauth_pubkey_memory(
180 &self,
181 username: &str,
182 pubkeydata: Option<&str>,
183 privatekeydata: &str,
184 passphrase: Option<&str>,
185 ) -> Result<(), Error> {
186 run_ssh2_fn(self.stream.as_ref().unwrap(), || {
187 self.inner
188 .userauth_pubkey_memory(username, pubkeydata, privatekeydata, passphrase)
189 })
190 .await
191 }
192
193 #[allow(missing_docs)]
195 pub async fn userauth_hostbased_file(
196 &self,
197 username: &str,
198 publickey: &Path,
199 privatekey: &Path,
200 passphrase: Option<&str>,
201 hostname: &str,
202 local_username: Option<&str>,
203 ) -> Result<(), Error> {
204 run_ssh2_fn(self.stream.as_ref().unwrap(), || {
205 self.inner.userauth_hostbased_file(
206 username,
207 publickey,
208 privatekey,
209 passphrase,
210 hostname,
211 local_username,
212 )
213 })
214 .await
215 }
216
217 pub fn authenticated(&self) -> bool {
219 self.inner.authenticated()
220 }
221
222 pub async fn auth_methods(&self, username: &str) -> Result<&str, Error> {
224 run_ssh2_fn(self.stream.as_ref().unwrap(), || {
225 self.inner.auth_methods(username)
226 })
227 .await
228 }
229
230 pub fn method_pref(&self, method_type: MethodType, prefs: &str) -> Result<(), Error> {
232 self.inner.method_pref(method_type, prefs)?;
233 Ok(())
234 }
235
236 pub fn methods(&self, method_type: MethodType) -> Option<&str> {
238 self.inner.methods(method_type)
239 }
240
241 pub fn supported_algs(&self, method_type: MethodType) -> Result<Vec<&'static str>, Error> {
243 self.inner.supported_algs(method_type).map_err(From::from)
244 }
245
246 pub fn agent(&self) -> Result<Agent, Error> {
248 let agent = self.inner.agent()?;
249 Ok(Agent::new(agent, self.stream.as_ref().unwrap().clone()))
250 }
251
252 pub fn known_hosts(&self) -> Result<KnownHosts, Error> {
254 self.inner.known_hosts().map_err(From::from)
255 }
256
257 pub async fn channel_session(&self) -> Result<Channel, Error> {
259 let channel = run_ssh2_fn(self.stream.as_ref().unwrap(), || {
260 self.inner.channel_session()
261 })
262 .await?;
263 Ok(Channel::new(channel, self.stream.as_ref().unwrap().clone()))
264 }
265
266 pub async fn channel_direct_tcpip(
268 &self,
269 host: &str,
270 port: u16,
271 src: Option<(&str, u16)>,
272 ) -> Result<Channel, Error> {
273 let channel = run_ssh2_fn(self.stream.as_ref().unwrap(), || {
274 self.inner.channel_direct_tcpip(host, port, src)
275 })
276 .await?;
277 Ok(Channel::new(channel, self.stream.as_ref().unwrap().clone()))
278 }
279
280 pub async fn channel_forward_listen(
282 &self,
283 remote_port: u16,
284 host: Option<&str>,
285 queue_maxsize: Option<u32>,
286 ) -> Result<(Listener, u16), Error> {
287 let (listener, port) = run_ssh2_fn(self.stream.as_ref().unwrap(), || {
288 self.inner
289 .channel_forward_listen(remote_port, host, queue_maxsize)
290 })
291 .await?;
292 Ok((
293 Listener::new(listener, self.stream.as_ref().unwrap().clone()),
294 port,
295 ))
296 }
297
298 pub async fn scp_recv(&self, path: &Path) -> Result<(Channel, ScpFileStat), Error> {
300 let (channel, file_stat) =
301 run_ssh2_fn(self.stream.as_ref().unwrap(), || self.inner.scp_recv(path)).await?;
302 Ok((
303 Channel::new(channel, self.stream.as_ref().unwrap().clone()),
304 file_stat,
305 ))
306 }
307
308 pub async fn scp_send(
310 &self,
311 remote_path: &Path,
312 mode: i32,
313 size: u64,
314 times: Option<(u64, u64)>,
315 ) -> Result<Channel, Error> {
316 let channel = run_ssh2_fn(self.stream.as_ref().unwrap(), || {
317 self.inner.scp_send(remote_path, mode, size, times)
318 })
319 .await?;
320 Ok(Channel::new(channel, self.stream.as_ref().unwrap().clone()))
321 }
322
323 pub async fn sftp(&self) -> Result<Sftp, Error> {
325 let sftp = run_ssh2_fn(self.stream.as_ref().unwrap(), || self.inner.sftp()).await?;
326 Ok(Sftp::new(sftp, self.stream.as_ref().unwrap().clone()))
327 }
328
329 pub async fn channel_open(
331 &self,
332 channel_type: &str,
333 window_size: u32,
334 packet_size: u32,
335 message: Option<&str>,
336 ) -> Result<Channel, Error> {
337 let channel = run_ssh2_fn(self.stream.as_ref().unwrap(), || {
338 self.inner
339 .channel_open(channel_type, window_size, packet_size, message)
340 })
341 .await?;
342 Ok(Channel::new(channel, self.stream.as_ref().unwrap().clone()))
343 }
344
345 pub fn banner(&self) -> Option<&str> {
347 self.inner.banner()
348 }
349
350 pub fn banner_bytes(&self) -> Option<&[u8]> {
352 self.inner.banner_bytes()
353 }
354
355 pub fn host_key(&self) -> Option<(&[u8], HostKeyType)> {
357 self.inner.host_key()
358 }
359
360 pub fn host_key_hash(&self, hash: HashType) -> Option<&[u8]> {
362 self.inner.host_key_hash(hash)
363 }
364
365 pub fn set_keepalive(&self, want_reply: bool, interval: u32) {
367 self.inner.set_keepalive(want_reply, interval)
368 }
369
370 pub async fn keepalive_send(&self) -> Result<u32, Error> {
372 run_ssh2_fn(self.stream.as_ref().unwrap(), || {
373 self.inner.keepalive_send()
374 })
375 .await
376 }
377
378 pub async fn disconnect(
380 &self,
381 reason: Option<DisconnectCode>,
382 description: &str,
383 lang: Option<&str>,
384 ) -> Result<(), Error> {
385 run_ssh2_fn(self.stream.as_ref().unwrap(), || {
386 self.inner.disconnect(reason, description, lang)
387 })
388 .await
389 }
390}
391
392#[cfg(unix)]
393impl AsRawFd for Session {
394 fn as_raw_fd(&self) -> RawFd {
395 self.inner.as_raw_fd()
396 }
397}
398
399#[cfg(windows)]
400impl AsRawSocket for Session {
401 fn as_raw_socket(&self) -> RawSocket {
402 self.inner.as_raw_socket()
403 }
404}