Crate thrussh

source ·
Expand description

Server and client SSH library, based on ring for its crypto, and tokio/futures for its network management. More information at pijul.org/thrussh.

Here is an example client and server:

extern crate thrussh;
extern crate thrussh_keys;
extern crate futures;
extern crate tokio;
extern crate env_logger;
use std::sync::Arc;
use thrussh::*;
use thrussh::server::{Auth, Session};
use thrussh_keys::*;

#[derive(Clone)]
struct Server {
 client_pubkey: Arc<thrussh_keys::key::PublicKey>
}

impl server::Server for Server {
   type Handler = Self;
   fn new(&self) -> Self {
       self.clone()
   }
}

impl server::Handler for Server {
   type Error = std::io::Error;
   type FutureAuth = futures::Finished<(Self, server::Auth), Self::Error>;
   type FutureUnit = futures::Finished<(Self, server::Session), Self::Error>;
   type FutureBool = futures::Finished<(Self, server::Session, bool), Self::Error>;

   fn finished_auth(self, auth: Auth) -> Self::FutureAuth {
       futures::finished((self, auth))
   }
   fn finished_bool(self, session: Session, b: bool) -> Self::FutureBool {
       futures::finished((self, session, b))
   }
   fn finished(self, session: Session) -> Self::FutureUnit {
       futures::finished((self, session))
   }

   fn auth_publickey(self, _: &str, _: &key::PublicKey) -> Self::FutureAuth {
       futures::finished((self, server::Auth::Accept))
   }
   fn data(self, channel: ChannelId, data: &[u8], mut session: server::Session) -> Self::FutureUnit {
       println!("data on channel {:?}: {:?}", channel, std::str::from_utf8(data));
       session.data(channel, None, data);
       futures::finished((self, session))
   }
}


use futures::Future;
use std::io::Read;


struct Client {
 key: Arc<thrussh_keys::key::KeyPair>
}

impl client::Handler for Client {
   type Error = ();
   type FutureBool = futures::Finished<(Self, bool), Self::Error>;
   type FutureUnit = futures::Finished<Self, Self::Error>;
   type FutureSign = futures::Finished<(Self, thrussh::CryptoVec), Self::Error>;
   type SessionUnit = futures::Finished<(Self, client::Session), Self::Error>;
   fn check_server_key(self, server_public_key: &key::PublicKey) -> Self::FutureBool {
       println!("check_server_key: {:?}", server_public_key);
       futures::finished((self, true))
   }
   fn channel_open_confirmation(self, channel: ChannelId, session: client::Session) -> Self::SessionUnit {
       println!("channel_open_confirmation: {:?}", channel);
       futures::finished((self, session))
   }
   fn data(self, channel: ChannelId, ext: Option<u32>, data: &[u8], session: client::Session) -> Self::SessionUnit {
       println!("data on channel {:?} {:?}: {:?}", ext, channel, std::str::from_utf8(data));
       futures::finished((self, session))
   }
}

impl Client {

 fn run(self, config: Arc<client::Config>, _: &str) {
    let key = self.key.clone();
    tokio::run(

      client::connect_future(
        "127.0.0.1:2222", config, None, self,
        |connection| {
          connection.authenticate_key("pe", key)
            .and_then(|session| {
              session.channel_open_session().and_then(|(session, channelid)| {
                session.data(channelid, None, "Hello, world!").and_then(|(mut session, _)| {
                  session.disconnect(Disconnect::ByApplication, "Ciao", "");
                  session
                })
              })
        })
      }).unwrap().map_err(|_| ())
    )
 }
}

fn main() {
   env_logger::init();
   // Starting the server thread.
   let client_key = thrussh_keys::key::KeyPair::generate_ed25519().unwrap();
   let client_pubkey = Arc::new(client_key.clone_public_key());
   let t = std::thread::spawn(|| {
       let mut config = thrussh::server::Config::default();
       config.connection_timeout = Some(std::time::Duration::from_secs(600));
       config.auth_rejection_time = std::time::Duration::from_secs(3);
       config.keys.push(thrussh_keys::key::KeyPair::generate_ed25519().unwrap());
       let config = Arc::new(config);
       let sh = Server{ client_pubkey };
       thrussh::server::run(config, "0.0.0.0:2222", sh);
   });

   std::thread::sleep(std::time::Duration::from_secs(1));
   let mut config = thrussh::client::Config::default();
   config.connection_timeout = Some(std::time::Duration::from_secs(600));
   let config = Arc::new(config);
   let sh = Client { key: Arc::new(client_key) };
   sh.run(config, "127.0.0.1:2222");

   // Kill the server thread after the client has ended.
   std::mem::forget(t)
}

Modules

Client side of this library.
Server side of this library.

Structs

The identifier of a channel.
A buffer which zeroes its memory on .clear(), .resize() and reallocations, to avoid copying secrets around.
The number of bytes read/written, and the number of seconds before a key re-exchange is requested.
Set of methods, represented by bit flags.
Lists of preferred algorithms. This is normally hard-coded into implementations.

Enums

Reason for not being able to open a channel.
A reason for disconnection.
Errors.
Errors including those coming from handler. These are not included in this crate’s “main” error type to allow for a simpler API (the “handler error” type cannot be inferred by the compiler in some functions).
Standard pseudo-terminal codes.
The type of signals that can be sent to a remote process. If you plan to use custom signals, read the RFC to understand the encoding.

Traits

Since handlers are large, their associated future types must implement this trait to provide reasonable default implementations (basically, rejecting all requests).
Named algorithms.
Types that have a “TCP shutdown” operation.