async_ssh/session/
mod.rs

1use tokio_io::{AsyncRead, AsyncWrite};
2use std::sync::Arc;
3use std::rc::Rc;
4use std::cell::RefCell;
5use futures::Future;
6use tokio_core::reactor::Handle;
7use channel;
8use {Connection, SharableConnection};
9use thrussh;
10use thrussh_keys;
11
12pub(crate) mod state;
13
14/// A newly established, unauthenticated SSH session.
15///
16/// All you can really do with this in authenticate it using one of the `authenticate_*` methods.
17/// You'll most likely want [`NewSession::authenticate_key`].
18pub struct NewSession<S: AsyncRead + AsyncWrite> {
19    c: Connection<S>,
20    handle: Handle,
21}
22
23impl<S: AsyncRead + AsyncWrite + 'static> NewSession<S> {
24    /// Authenticate as the given user using the given keypair.
25    ///
26    /// See also
27    /// [`thrussh::client::Connection::authenticate_key`](https://docs.rs/thrussh/0.19/thrussh/client/struct.Connection.html#method.authenticate_key).
28    pub fn authenticate_key(
29        self,
30        user: &str,
31        key: thrussh_keys::key::KeyPair,
32    ) -> Box<Future<Item = Session<S>, Error = thrussh::HandlerError<()>>>
33    where
34        S: thrussh::Tcp,
35    {
36        let NewSession { c, handle } = self;
37        Box::new(
38            c.c
39                .authenticate_key(user, key)
40                .map(move |c| Session::make(Connection { c, task: None }, handle)),
41        )
42    }
43}
44
45/// An established and authenticated SSH session.
46///
47/// You can use this session to execute commands on the remote host using [`Session::open_exec`].
48/// This will give you back a [`Channel`], which can be used to read from the resulting process'
49/// `STDOUT`, or to write the the process' `STDIN`.
50pub struct Session<S: AsyncRead + AsyncWrite>(SharableConnection<S>);
51
52impl<S: AsyncRead + AsyncWrite + thrussh::Tcp + 'static> Session<S> {
53    /// Establish a new SSH session on top of the given stream.
54    ///
55    /// The resulting SSH session is initially unauthenticated (see [`NewSession`]), and must be
56    /// authenticated before it becomes useful.
57    ///
58    /// Note that the reactor behind the given `handle` *must* continue to be driven for any
59    /// channels created from this [`Session`] to work.
60    pub fn new(stream: S, handle: &Handle) -> Result<NewSession<S>, thrussh::HandlerError<()>> {
61        thrussh::client::Connection::new(Arc::default(), stream, state::Ref::default(), None)
62            .map(|c| NewSession {
63                c: Connection { c, task: None },
64                handle: handle.clone(),
65            })
66            .map_err(thrussh::HandlerError::Error)
67    }
68
69    fn make(c: Connection<S>, handle: Handle) -> Self {
70        let c = SharableConnection(Rc::new(RefCell::new(c)));
71        handle.spawn(c.clone());
72        Session(c)
73    }
74
75    /// Retrieve the last error encountered during this session.
76    ///
77    /// Note that it is unlikely you will be able to use any items associated with this session
78    /// once it has returned an error.
79    ///
80    /// Calling this method clears the error.
81    pub fn last_error(&mut self) -> Option<thrussh::HandlerError<()>> {
82        let connection = (self.0).0.borrow();
83        let handler = connection.c.handler();
84        let mut state = handler.borrow_mut();
85        state.errored_with.take()
86    }
87
88    /// Establish a new channel over this session to execute the given command.
89    ///
90    /// Note that any errors encountered while operating on the channel after it has been opened
91    /// will manifest only as reads or writes no longer succeeding. To get the underlying error,
92    /// call [`Session::last_error`].
93    pub fn open_exec<'a>(&mut self, cmd: &'a str) -> channel::ChannelOpenFuture<'a, S> {
94        let mut session = (self.0).0.borrow_mut();
95        let state = session.c.handler().clone();
96
97        let channel_id = (&mut *session.c)
98            .channel_open_session()
99            .expect("sessions are always authenticated");
100        state
101            .borrow_mut()
102            .state_for
103            .insert(channel_id, channel::State::default());
104        channel::ChannelOpenFuture::new(cmd, self.0.clone(), state, channel_id)
105    }
106}