use crate::base::err_utils::encpasulate_error; use bytes::BufMut; use clap::Parser; /// TCP relay client #[derive(Parser, Debug, Clone, Default)] #[clap(author, version, about, long_about = None)] pub struct ClientConfig { /// Access token #[clap(short, long)] pub token: Option, /// Relay server #[clap(short, long, default_value = "http://127.0.0.1:8000")] pub relay_url: String, /// Listen address #[clap(short, long, default_value = "127.0.0.1")] pub listen_address: String, /// Alternative root certificate to use for server authentication #[clap(short = 'c', long)] pub root_certificate: Option, /// TLS certificate for TLS authentication. #[clap(long)] pub tls_cert: Option, /// TLS key for TLS authentication. #[clap(long)] pub tls_key: Option, #[clap(skip)] pub _keys_cache: KeysCache, } #[derive(Parser, Debug, Clone, Default)] pub struct KeysCache { #[clap(skip)] _root_certificate_cache: Option>, #[clap(skip)] _tls_cert_cache: Option>, #[clap(skip)] _tls_key_cache: Option>, } impl ClientConfig { /// Load certificates and put them in cache pub fn load_certificates(&mut self) -> std::io::Result<()> { self._keys_cache = KeysCache { _root_certificate_cache: load_pem_file(&self.root_certificate, "root certificate")?, _tls_cert_cache: load_pem_file(&self.tls_cert, "client certificate")?, _tls_key_cache: load_pem_file(&self.tls_key, "client key")?, }; Ok(()) } /// Get client token, returning a dummy token if none was specified pub fn get_auth_token(&self) -> &str { self.token.as_deref().unwrap_or("none") } /// Get root certificate content pub fn get_root_certificate(&self) -> Option> { self._keys_cache._root_certificate_cache.clone() } /// Get client certificate & key pair, if available pub fn get_client_keypair(&self) -> Option<(&Vec, &Vec)> { if let (Some(cert), Some(key)) = ( &self._keys_cache._tls_cert_cache, &self._keys_cache._tls_key_cache, ) { Some((cert, key)) } else { None } } /// Get client certificate & key pair, in a single memory buffer pub fn get_merged_client_keypair(&self) -> Option> { self.get_client_keypair().map(|(c, k)| { let mut out = k.to_vec(); out.put_slice("\n".as_bytes()); out.put_slice(c); out }) } } fn load_pem_file(path: &Option, name: &str) -> std::io::Result>> { Ok(match path { None => None, Some(p) => Some( std::fs::read(p) .map_err(|e| encpasulate_error(e, format!("Failed to load {}!", name)))?, ), }) } #[cfg(test)] mod test { use crate::tcp_relay_client::client_config::ClientConfig; #[test] fn verify_cli() { use clap::CommandFactory; ClientConfig::command().debug_assert() } }