1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
//! High-level library for asynchronous SSH connections.
//!
//! This crate presents a higher-level asynchronous interface for issuing commands over SSH
//! connections. It's built on top of [thrussh](https://pijul.org/thrussh/), a pure-Rust
//! implementation of the SSH protocol.
//!
//! At its core, the crate provides the notion of an SSH [`Session`], which can have zero or more
//! [`Channel`]s. Each [`Channel`] executes some user-defined command on the remote machine, and
//! implements [`AsyncRead`](https://docs.rs/tokio-io/0.1/tokio_io/trait.AsyncRead.html) and
//! (eventually) [`AsyncWrite`](https://docs.rs/tokio-io/0.1/tokio_io/trait.AsyncWrite.html) to
//! allow reading from and writing to the remote process respectively. For those unfamiliar with
//! asynchronous I/O, you'll likely want to start with the [functions in
//! `tokio-io::io`](https://docs.rs/tokio-io/0.1/tokio_io/io/index.html#functions).
//!
//! The code is currently in a pre-alpha stage, with only a subset of the core features
//! implemented, and with fairly gaping API holes like `thrussh` types being exposed all over
//! the place or error types not being nice to work with.
//!
//! # Examples
//!
//! ```no_run
//! # extern crate tokio_core;
//! # extern crate async_ssh;
//! # extern crate thrussh_keys;
//! # extern crate thrussh;
//! # extern crate tokio_io;
//! # extern crate futures;
//! # use async_ssh::*;
//! # use futures::Future;
//! # fn main() {
//! let key = thrussh_keys::load_secret_key("/path/to/key", None).unwrap();
//!
//! let mut core = tokio_core::reactor::Core::new().unwrap();
//! let handle = core.handle();
//! let ls_out = tokio_core::net::TcpStream::connect(&"127.0.0.1:22".parse().unwrap(), &handle)
//!     .map_err(thrussh::Error::IO)
//!     .map_err(thrussh::HandlerError::Error)
//!     .and_then(|c| Session::new(c, &handle))
//!     .and_then(|session| session.authenticate_key("username", key))
//!     .and_then(|mut session| session.open_exec("ls -la"));
//!
//! let channel = core.run(ls_out).unwrap();
//! let (channel, data) = core.run(tokio_io::io::read_to_end(channel, Vec::new())).unwrap();
//! let status = core.run(channel.exit_status()).unwrap();
//!
//! println!("{}", ::std::str::from_utf8(&data[..]).unwrap());
//! println!("exited with: {}", status);
//! # }
//! ```
#![deny(missing_docs)]

extern crate futures;
extern crate thrussh;
extern crate thrussh_keys;
extern crate tokio_core;
extern crate tokio_io;

use tokio_io::{AsyncRead, AsyncWrite};
use std::rc::Rc;
use std::cell::RefCell;
use futures::Future;

mod session;
mod channel;

pub use channel::{Channel, ChannelOpenFuture, ExitStatusFuture};
pub use session::{NewSession, Session};

struct Connection<S: AsyncRead + AsyncWrite> {
    c: thrussh::client::Connection<S, session::state::Ref>,
    task: Option<futures::task::Task>,
}

struct SharableConnection<S: AsyncRead + AsyncWrite>(Rc<RefCell<Connection<S>>>);
impl<S> Clone for SharableConnection<S>
where
    S: AsyncRead + AsyncWrite,
{
    fn clone(&self) -> Self {
        SharableConnection(self.0.clone())
    }
}

impl<S: AsyncRead + AsyncWrite + thrussh::Tcp> Future for SharableConnection<S> {
    type Item = ();
    type Error = ();

    fn poll(&mut self) -> futures::Poll<Self::Item, Self::Error> {
        // NOTE: SessionStateRef as Handler cannot use Rc<RefMut<C<S>>>
        let mut c = self.0.borrow_mut();
        c.task = Some(futures::task::current());
        match c.c.poll() {
            Ok(r) => Ok(r),
            Err(e) => {
                let state = self.0.borrow();
                let state = state.c.handler();
                let mut state = state.borrow_mut();
                state.errored_with = Some(e);
                Err(())
            }
        }
    }
}