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