1mod algos;
6
7use std::collections::HashMap;
8
9pub use self::algos::Algorithms;
10pub(crate) use self::algos::AlgorithmsRule;
11use super::{Duration, PathBuf};
12use crate::DefaultAlgorithms;
13
14#[derive(Debug, Clone, PartialEq, Eq)]
18pub struct HostParams {
19 pub add_keys_to_agent: Option<bool>,
21 pub bind_address: Option<String>,
23 pub bind_interface: Option<String>,
25 pub ca_signature_algorithms: Algorithms,
27 pub certificate_file: Option<PathBuf>,
29 pub ciphers: Algorithms,
31 pub compression: Option<bool>,
33 pub connection_attempts: Option<usize>,
35 pub connect_timeout: Option<Duration>,
37 pub forward_agent: Option<bool>,
39 pub host_key_algorithms: Algorithms,
41 pub host_name: Option<String>,
43 pub identity_file: Option<Vec<PathBuf>>,
47 pub ignore_unknown: Option<Vec<String>>,
49 pub kex_algorithms: Algorithms,
51 pub mac: Algorithms,
53 pub port: Option<u16>,
55 pub proxy_jump: Option<Vec<String>>,
57 pub pubkey_accepted_algorithms: Algorithms,
59 pub pubkey_authentication: Option<bool>,
61 pub remote_forward: Option<u16>,
63 pub server_alive_interval: Option<Duration>,
65 pub tcp_keep_alive: Option<bool>,
67 #[cfg(target_os = "macos")]
68 pub use_keychain: Option<bool>,
70 pub user: Option<String>,
72 pub ignored_fields: HashMap<String, Vec<String>>,
74 pub unsupported_fields: HashMap<String, Vec<String>>,
76}
77
78impl HostParams {
79 pub fn new(default_algorithms: &DefaultAlgorithms) -> Self {
81 Self {
82 add_keys_to_agent: None,
83 bind_address: None,
84 bind_interface: None,
85 ca_signature_algorithms: Algorithms::new(&default_algorithms.ca_signature_algorithms),
86 certificate_file: None,
87 ciphers: Algorithms::new(&default_algorithms.ciphers),
88 compression: None,
89 connection_attempts: None,
90 connect_timeout: None,
91 forward_agent: None,
92 host_key_algorithms: Algorithms::new(&default_algorithms.host_key_algorithms),
93 host_name: None,
94 identity_file: None,
95 ignore_unknown: None,
96 kex_algorithms: Algorithms::new(&default_algorithms.kex_algorithms),
97 mac: Algorithms::new(&default_algorithms.mac),
98 port: None,
99 proxy_jump: None,
100 pubkey_accepted_algorithms: Algorithms::new(
101 &default_algorithms.pubkey_accepted_algorithms,
102 ),
103 pubkey_authentication: None,
104 remote_forward: None,
105 server_alive_interval: None,
106 tcp_keep_alive: None,
107 #[cfg(target_os = "macos")]
108 use_keychain: None,
109 user: None,
110 ignored_fields: HashMap::new(),
111 unsupported_fields: HashMap::new(),
112 }
113 }
114
115 pub(crate) fn ignored(&self, param: &str) -> bool {
117 self.ignore_unknown
118 .as_ref()
119 .map(|x| x.iter().any(|x| x.as_str() == param))
120 .unwrap_or(false)
121 }
122
123 pub fn overwrite_if_none(&mut self, b: &Self) {
125 self.add_keys_to_agent = self.add_keys_to_agent.or(b.add_keys_to_agent);
126 self.bind_address = self.bind_address.clone().or_else(|| b.bind_address.clone());
127 self.bind_interface = self
128 .bind_interface
129 .clone()
130 .or_else(|| b.bind_interface.clone());
131 self.certificate_file = self
132 .certificate_file
133 .clone()
134 .or_else(|| b.certificate_file.clone());
135 self.compression = self.compression.or(b.compression);
136 self.connection_attempts = self.connection_attempts.or(b.connection_attempts);
137 self.connect_timeout = self.connect_timeout.or(b.connect_timeout);
138 self.forward_agent = self.forward_agent.or(b.forward_agent);
139 self.host_name = self.host_name.clone().or_else(|| b.host_name.clone());
140 self.identity_file = self
141 .identity_file
142 .clone()
143 .or_else(|| b.identity_file.clone());
144 self.ignore_unknown = self
145 .ignore_unknown
146 .clone()
147 .or_else(|| b.ignore_unknown.clone());
148 self.port = self.port.or(b.port);
149 self.proxy_jump = self.proxy_jump.clone().or_else(|| b.proxy_jump.clone());
150 self.pubkey_authentication = self.pubkey_authentication.or(b.pubkey_authentication);
151 self.remote_forward = self.remote_forward.or(b.remote_forward);
152 self.server_alive_interval = self.server_alive_interval.or(b.server_alive_interval);
153 #[cfg(target_os = "macos")]
154 {
155 self.use_keychain = self.use_keychain.or(b.use_keychain);
156 }
157 self.tcp_keep_alive = self.tcp_keep_alive.or(b.tcp_keep_alive);
158 self.user = self.user.clone().or_else(|| b.user.clone());
159 for (ignored_field, args) in &b.ignored_fields {
160 if !self.ignored_fields.contains_key(ignored_field) {
161 self.ignored_fields
162 .insert(ignored_field.to_owned(), args.to_owned());
163 }
164 }
165 for (unsupported_field, args) in &b.unsupported_fields {
166 if !self.unsupported_fields.contains_key(unsupported_field) {
167 self.unsupported_fields
168 .insert(unsupported_field.to_owned(), args.to_owned());
169 }
170 }
171
172 if self.ca_signature_algorithms.is_default() && !b.ca_signature_algorithms.is_default() {
174 self.ca_signature_algorithms = b.ca_signature_algorithms.clone();
175 }
176 if self.ciphers.is_default() && !b.ciphers.is_default() {
177 self.ciphers = b.ciphers.clone();
178 }
179 if self.host_key_algorithms.is_default() && !b.host_key_algorithms.is_default() {
180 self.host_key_algorithms = b.host_key_algorithms.clone();
181 }
182 if self.kex_algorithms.is_default() && !b.kex_algorithms.is_default() {
183 self.kex_algorithms = b.kex_algorithms.clone();
184 }
185 if self.mac.is_default() && !b.mac.is_default() {
186 self.mac = b.mac.clone();
187 }
188 if self.pubkey_accepted_algorithms.is_default()
189 && !b.pubkey_accepted_algorithms.is_default()
190 {
191 self.pubkey_accepted_algorithms = b.pubkey_accepted_algorithms.clone();
192 }
193 }
194}
195
196#[cfg(test)]
197mod tests {
198
199 use std::str::FromStr;
200
201 use pretty_assertions::assert_eq;
202
203 use super::*;
204 use crate::params::algos::AlgorithmsRule;
205
206 #[test]
207 fn should_initialize_params() {
208 let params = HostParams::new(&DefaultAlgorithms::default());
209 assert!(params.add_keys_to_agent.is_none());
210 assert!(params.bind_address.is_none());
211 assert!(params.bind_interface.is_none());
212 assert_eq!(
213 params.ca_signature_algorithms.algorithms(),
214 DefaultAlgorithms::default().ca_signature_algorithms
215 );
216 assert!(params.certificate_file.is_none());
217 assert_eq!(
218 params.ciphers.algorithms(),
219 DefaultAlgorithms::default().ciphers
220 );
221 assert!(params.compression.is_none());
222 assert!(params.connection_attempts.is_none());
223 assert!(params.connect_timeout.is_none());
224 assert!(params.forward_agent.is_none());
225 assert_eq!(
226 params.host_key_algorithms.algorithms(),
227 DefaultAlgorithms::default().host_key_algorithms
228 );
229 assert!(params.host_name.is_none());
230 assert!(params.identity_file.is_none());
231 assert!(params.ignore_unknown.is_none());
232 assert_eq!(
233 params.kex_algorithms.algorithms(),
234 DefaultAlgorithms::default().kex_algorithms
235 );
236 assert_eq!(params.mac.algorithms(), DefaultAlgorithms::default().mac);
237 assert!(params.port.is_none());
238 assert!(params.proxy_jump.is_none());
239 assert_eq!(
240 params.pubkey_accepted_algorithms.algorithms(),
241 DefaultAlgorithms::default().pubkey_accepted_algorithms
242 );
243 assert!(params.pubkey_authentication.is_none());
244 assert!(params.remote_forward.is_none());
245 assert!(params.server_alive_interval.is_none());
246 #[cfg(target_os = "macos")]
247 assert!(params.use_keychain.is_none());
248 assert!(params.tcp_keep_alive.is_none());
249 }
250
251 #[test]
252 fn test_should_overwrite_if_none() {
253 let mut params = HostParams::new(&DefaultAlgorithms::default());
254 params.bind_address = Some(String::from("pippo"));
255
256 let mut b = HostParams::new(&DefaultAlgorithms::default());
257 b.bind_address = Some(String::from("pluto"));
258 b.bind_interface = Some(String::from("tun0"));
259 b.ciphers
260 .apply(AlgorithmsRule::from_str("c,d").expect("parse error"));
261
262 params.overwrite_if_none(&b);
263 assert_eq!(params.bind_address.unwrap(), "pippo");
264 assert_eq!(params.bind_interface.unwrap(), "tun0");
265
266 assert_eq!(
268 params.ciphers.algorithms(),
269 vec!["c".to_string(), "d".to_string()]
270 );
271 }
272}