utils_box/connections/
ssh_client.rs1use anyhow::{Result, bail};
6use ssh2::Session;
7use std::{
8 fs::File, io::Read, io::Write, net::TcpStream, os::unix::prelude::PermissionsExt, path::PathBuf,
9};
10
11use crate::{log_info, log_trace};
12
13#[derive(Clone)]
14pub struct SshClient {
15 server_ip: String,
16 server_port: u16,
17 ssh_session: Session,
18}
19
20impl SshClient {
21 pub fn new(
22 server_ip: String,
23 server_port: u16,
24 username: String,
25 password: String,
26 ) -> Result<Self> {
27 let tcp_stream = TcpStream::connect(format!("{server_ip}:{server_port}"))?;
29
30 let mut ssh_session = Session::new()?;
31 ssh_session.set_tcp_stream(tcp_stream);
32 ssh_session.handshake()?;
33
34 ssh_session.userauth_password(&username, &password)?;
35
36 if !ssh_session.authenticated() {
37 bail!("[SshClient][new] Authentication FAILED!");
38 }
39
40 Ok(SshClient {
41 server_ip,
42 server_port,
43 ssh_session,
44 })
45 }
46
47 pub fn local(username: String, password: String) -> Result<Self> {
48 Self::new("127.0.0.1".to_string(), 22, username, password)
49 }
50
51 pub fn connection_info(&self) -> (String, u16) {
52 (self.server_ip.clone(), self.server_port)
53 }
54
55 pub fn upload(&self, file: PathBuf, remote_file: PathBuf) -> Result<()> {
56 let mut local_file = File::open(&file)?;
58 let local_permissions = local_file.metadata()?.permissions().mode() as i32 & 0o777;
60
61 let mut file_stream = vec![];
62 local_file.read_to_end(&mut file_stream)?;
63
64 log_info!(
65 "[SshClient][upload] [{} => {}] Permissions:[{:o}] Size: [{} Bytes]",
66 file.display(),
67 remote_file.display(),
68 local_permissions,
69 file_stream.len(),
70 );
71
72 let mut remote_file = self.ssh_session.scp_send(
74 &remote_file,
75 local_permissions,
76 file_stream.len() as u64,
77 None,
78 )?;
79
80 remote_file.write_all(&file_stream)?;
81
82 std::thread::sleep(std::time::Duration::from_secs(3));
84
85 remote_file.flush()?;
86
87 remote_file.send_eof()?;
88
89 remote_file.close()?;
91
92 Ok(())
93 }
94
95 pub fn download(&self, remote_file: PathBuf, file: PathBuf) -> Result<()> {
96 let mut local = File::create(&file)?;
98
99 let (mut remote, remote_stats) = self.ssh_session.scp_recv(&remote_file)?;
101
102 log_info!(
103 "[SshClient][download] [{} => {}] Permissions:[{:o}] Size: [{} Bytes]",
104 remote_file.display(),
105 file.display(),
106 remote_stats.mode(),
107 remote_stats.size(),
108 );
109
110 let mut file_stream = vec![];
112 remote.read_to_end(&mut file_stream)?;
113
114 remote.send_eof()?;
116 remote.wait_eof()?;
117 remote.close()?;
118 remote.wait_close()?;
119
120 local.write_all(&file_stream)?;
122
123 std::thread::sleep(std::time::Duration::from_secs(3));
125 local.flush()?;
126
127 local
129 .metadata()?
130 .permissions()
131 .set_mode(remote_stats.mode() as u32);
132
133 Ok(())
134 }
135
136 pub fn execute_cmd(&self, cmd: &str) -> Result<String> {
137 let mut channel = self.ssh_session.channel_session()?;
139 channel.exec(cmd)?;
140
141 let mut std_out = String::new();
143 channel.read_to_string(&mut std_out)?;
144 log_trace!("[SshClient][execute_cmd] STDOUT: \n{}", std_out);
145
146 channel.close()?;
148 channel.wait_close()?;
149
150 Ok(std_out)
151 }
152}