duxcore/connection/connectionmode/
ssh2mode.rs1use crate::connection::specification::Credentials;
4use crate::error::Error;
5use crate::result::cmd::CmdResult;
6use pem::Pem;
7use serde::{Deserialize, Serialize};
8use ssh2::Session;
9use std::io::Read;
10use std::net::TcpStream;
11use std::path::PathBuf;
12
13#[derive(Debug, Clone, Serialize, Deserialize)]
14pub struct Ssh2ConnectionDetails {
15 pub hostaddress: String,
16 pub authmode: Ssh2AuthMode,
17}
18
19impl Ssh2ConnectionDetails {
20 pub fn from(hostaddress: String, authmode: Ssh2AuthMode) -> Ssh2ConnectionDetails {
21 Ssh2ConnectionDetails {
22 hostaddress,
23 authmode,
24 }
25 }
26}
27
28#[derive(Clone)]
29pub struct Ssh2HostHandler {
30 pub hostaddress: String,
31 pub sshsession: Session,
32 pub authmode: Ssh2AuthMode,
33}
34
35impl Ssh2HostHandler {
36 pub fn new() -> Ssh2HostHandler {
37 Ssh2HostHandler {
38 hostaddress: String::new(),
39 sshsession: Session::new().unwrap(),
40 authmode: Ssh2AuthMode::Unset,
41 }
42 }
43
44 pub fn none() -> Ssh2HostHandler {
45 Ssh2HostHandler {
46 hostaddress: String::from(""),
47 sshsession: Session::new().unwrap(), authmode: Ssh2AuthMode::Unset,
49 }
50 }
51
52 pub fn from(hostaddress: String, authmode: Ssh2AuthMode) -> Ssh2HostHandler {
53 Ssh2HostHandler {
54 hostaddress,
55 sshsession: Session::new().unwrap(),
56 authmode,
57 }
58 }
59
60 pub fn set_to(&mut self, hostaddress: String, authmode: Ssh2AuthMode) {
61 self.hostaddress = hostaddress;
62 self.authmode = authmode;
63 }
64
65 pub fn init(&mut self) -> Result<(), Error> {
66 if self.authmode == Ssh2AuthMode::Unset {
67 return Err(Error::MissingInitialization(
68 "SSH2 authentication mode is unset".to_string(),
69 ));
70 } else {
71 match TcpStream::connect(format!("{}:22", self.hostaddress)) {
73 Ok(tcp) => {
74 self.sshsession.set_tcp_stream(tcp);
75 self.sshsession.handshake().unwrap();
76
77 match &self.authmode {
78 Ssh2AuthMode::UsernamePassword(credentials) => {
79 self.sshsession
80 .userauth_password(&credentials.username, &credentials.password)
81 .unwrap();
82 if self.sshsession.authenticated() {
83 return Ok(());
84 } else {
85 return Err(Error::FailedInitialization(String::from(
86 "PLACEHOLDER",
87 )));
88 }
89 }
90 Ssh2AuthMode::KeyFile((username, privatekeypath)) => {
91 self.sshsession
92 .userauth_pubkey_file(
93 username.as_str(),
94 None,
95 &privatekeypath,
96 None,
97 )
98 .unwrap(); if self.sshsession.authenticated() {
100 return Ok(());
101 } else {
102 return Err(Error::FailedInitialization(String::from(
103 "PLACEHOLDER",
104 )));
105 }
106 }
107 Ssh2AuthMode::KeyMemory((username, pem)) => {
108 self.sshsession
109 .userauth_pubkey_memory(
110 username.as_str(),
111 None,
112 pem.to_string().as_str(), None,
114 )
115 .unwrap(); if self.sshsession.authenticated() {
117 return Ok(());
118 } else {
119 return Err(Error::FailedInitialization(String::from(
120 "PLACEHOLDER",
121 )));
122 }
123 }
124 Ssh2AuthMode::Agent(_agent) => {
125 return Ok(());
126 } _ => return Err(Error::FailedInitialization(String::from("Other error"))),
128 }
129 }
130 Err(e) => {
131 return Err(Error::FailedTcpBinding(format!("{:?}", e)));
132 }
133 }
134 }
135 }
136
137 pub fn is_this_cmd_available(&self, cmd: &str) -> Result<bool, Error> {
138 let check_cmd_content = format!("command -v {}", cmd);
139 let check_cmd_result = self.run_cmd(check_cmd_content.as_str());
140
141 match check_cmd_result {
142 Ok(cmd_result) => {
143 if cmd_result.rc == 0 {
144 return Ok(true);
145 } else {
146 return Ok(false);
147 }
148 }
149 Err(e) => {
150 return Err(Error::FailureToRunCommand(format!("{:?}", e)));
151 }
152 }
153 }
154
155 pub fn run_cmd(&self, cmd: &str) -> Result<CmdResult, Error> {
156 if let Ssh2AuthMode::Unset = self.authmode {
157 return Err(Error::MissingInitialization(
158 "Can't run command on remote host : authentication unset".to_string(),
159 ));
160 }
161
162 match self.sshsession.channel_session() {
163 Ok(mut channel) => {
164 channel.exec(cmd).unwrap();
165 let mut s = String::new();
166 channel.read_to_string(&mut s).unwrap();
167 channel.wait_close().unwrap();
168
169 return Ok(CmdResult {
170 rc: channel.exit_status().unwrap(),
171 stdout: s,
172 });
173 }
174 Err(e) => {
175 return Err(Error::FailureToEstablishConnection(format!("{e}")));
176 }
177 }
178 }
179}
180
181#[derive(Clone, PartialEq, Serialize, Deserialize)]
182pub enum Ssh2AuthMode {
183 Unset,
184 UsernamePassword(Credentials),
185 KeyFile((String, PathBuf)), KeyMemory((String, Pem)), Agent(String), }
189
190impl std::fmt::Debug for Ssh2AuthMode {
191 fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
192 match self {
193 Ssh2AuthMode::Unset => {
194 write!(f, "Unset")
195 }
196 Ssh2AuthMode::UsernamePassword(creds) => {
197 write!(f, "UsernamePassword(Credentials {{ username: {:?}, password: \"HIDDEN PASSWORD\" }})", creds.username)
198 }
199 Ssh2AuthMode::KeyFile((username, key_path)) => {
200 write!(f, "KeyFile(({:?}, {:?}))", username, key_path)
201 }
202 Ssh2AuthMode::KeyMemory((username, _key_content)) => {
203 write!(f, "KeyMemory(({:?}, \"HIDDEN KEY CONTENT\"))", username)
204 }
205 Ssh2AuthMode::Agent(agent_name) => {
206 write!(f, "Agent({:?})", agent_name)
207 }
208 }
209 }
210}