use failure::Error;
use std::collections::HashMap;
use ssh2::{Session, ScpFileStat, Channel};
use std::fs::File;
use std::io::prelude::*;
use std::io::BufReader;
use std::net::TcpStream;
use std::path::Path;
use std::str;
use std::string::String;
const SCPMODE: i32 = 0o644;
pub struct SSH {
session: Option<Session>,
host: String,
port: u16,
}
impl SSH {
pub fn new(host: &str, port: u16) -> Self {
Self {
session: None,
host: host.to_owned(),
port: port,
}
}
fn sess_ref(&self) -> &Session {
self.session.as_ref().unwrap()
}
fn create_socket(&self) -> Result<Session, Error> {
let socket = TcpStream::connect(format!("{}:{}", self.host, self.port))?;
let mut sess = Session::new()?;
sess.set_tcp_stream(socket);
sess.handshake()?;
Ok(sess)
}
pub fn identities() -> Result<HashMap<String, Vec<u8>>, Error> {
let sess = Session::new().unwrap();
let mut agent = sess.agent().unwrap();
agent.connect().unwrap();
agent.list_identities().unwrap();
let mut identities = HashMap::new();
for identity in agent.identities() {
let identity = identity.unwrap(); identities.insert(
identity.comment().to_owned(),
identity.blob().to_owned());
}
Ok(identities)
}
pub fn connect(&mut self, username: &str, pass: &str) -> Result<(), Error> {
let sess = self.create_socket()?;
sess.userauth_password(username, pass)?;
assert!(sess.authenticated()); self.session = Some(sess);
Ok(())
}
pub fn connect_agent(&mut self, username:&str) -> Result<(), Error> {
let sess = self.create_socket()?;
sess.userauth_agent(username)?;
assert!(sess.authenticated()); self.session = Some(sess);
Ok(())
}
pub fn authed(&self) -> bool {
self.sess_ref().authenticated()
}
pub fn keepalive(&mut self, reply: bool, interval: u32) -> Result<(), Error> {
self.sess_ref().set_keepalive(reply, interval);
self.sess_ref().keepalive_send()?;
Ok(())
}
pub fn tunnel(&mut self, host: &str, port: u16, dst: Option<(&str, u16)>) -> Result<Channel, Error> {
assert_eq!(self.authed(), true);
let sess = self.sess_ref();
let channel = sess.channel_direct_tcpip(host, port, dst).unwrap();
Ok(channel)
}
pub fn forward(&mut self, host: &str, port: u16, dst: Option<(&str, u16)>) -> Result<Channel, Error> {
assert_eq!(self.authed(), true);
let sess = self.sess_ref();
let channel = sess.channel_direct_tcpip(host, port, dst).unwrap();
Ok(channel)
}
pub fn get_shell(&self) -> Result<(), Error> {
let mut channel = self.sess_ref().channel_session()?;
channel.request_pty("xterm", None, None)?;
channel.shell()?;
channel.close()?;
Ok(())
}
pub fn run_command(&self, cmd: &str) -> Result<String, Error> {
let mut channel = self.sess_ref().channel_session()?;
channel.exec(cmd)?;
let mut stdout = String::new();
channel.read_to_string(&mut stdout)?;
Ok(stdout)
}
pub fn upload_file(&self, fpath: &Path, dest: &Path) -> Result<(), Error> {
let sess = self.sess_ref();
let file = File::open(fpath)?;
let mut reader = BufReader::new(file);
let data = reader.fill_buf()?;
let data_len = data.len() as u64;
sess.scp_send(dest, SCPMODE, data_len, None)
.unwrap()
.write(data)?;
Ok(())
}
pub fn get_file(&self, fpath: &Path) -> Result<(Vec<u8>, ScpFileStat), Error> {
let sess = self.session.as_ref().unwrap();
let (mut remote_file, stat) = sess.scp_recv(fpath)?;
let mut contents = Vec::new();
remote_file.read_to_end(&mut contents)?;
Ok((contents, stat))
}
}