From c15db6f83353d4befb85b0873a72df071d95ca99 Mon Sep 17 00:00:00 2001 From: Jake McGinty Date: Sat, 10 Apr 2021 17:38:59 +0900 Subject: [PATCH] client: don't leave interface behind on failed install --- client/src/main.rs | 91 +++++++++++++++++++++++++++------------------- client/src/util.rs | 10 ++++- 2 files changed, 61 insertions(+), 40 deletions(-) diff --git a/client/src/main.rs b/client/src/main.rs index 1a498d8..cc2614d 100644 --- a/client/src/main.rs +++ b/client/src/main.rs @@ -183,7 +183,7 @@ fn update_hosts_file( fn install(invite: &Path, hosts_file: Option) -> Result<(), Error> { shared::ensure_dirs_exist(&[*CLIENT_CONFIG_PATH])?; - let mut config = InterfaceConfig::from_file(invite)?; + let config = InterfaceConfig::from_file(invite)?; let iface = Input::with_theme(&*prompts::THEME) .with_prompt("Interface name") @@ -196,7 +196,59 @@ fn install(invite: &Path, hosts_file: Option) -> Result<(), Error> { } let iface = iface.parse()?; + redeem_invite(&iface, config, target_conf).map_err(|e| { + println!("{} bringing down the interface.", "[*]".dimmed()); + if let Err(e) = wg::down(&iface) { + println!("{} failed to bring down interface: {}.", "[*]".yellow(), e.to_string()); + }; + println!("{} Failed to redeem invite. Now's a good time to make sure the server is started and accessible!", "[!]".red()); + e + })?; + fetch(&iface, false, hosts_file)?; + + if Confirm::with_theme(&*prompts::THEME) + .with_prompt(&format!( + "Delete invitation file \"{}\" now? (It's no longer needed)", + invite.to_string_lossy().yellow() + )) + .default(true) + .interact()? + { + std::fs::remove_file(invite).with_path(invite)?; + } + + printdoc!( + " + {star} Done! + + {interface} has been {installed}. + + It's recommended to now keep the interface automatically refreshing via systemd: + + {systemctl_enable}{interface} + + By default, innernet will write to your /etc/hosts file for peer name + resolution. To disable this behavior, use the --no-write-hosts or --write-hosts [PATH] + options. + + See the manpage or innernet GitHub repo for more detailed instruction on managing your + interface and network. Have fun! + + ", + star = "[*]".dimmed(), + interface = iface.to_string().yellow(), + installed = "installed".green(), + systemctl_enable = "systemctl enable --now innernet@".yellow(), + ); + Ok(()) +} + +fn redeem_invite( + iface: &InterfaceName, + mut config: InterfaceConfig, + target_conf: PathBuf, +) -> Result<(), Error> { println!("{} bringing up the interface.", "[*]".dimmed()); wg::up( &iface, @@ -243,43 +295,6 @@ fn install(invite: &Path, hosts_file: Option) -> Result<(), Error> { .set_private_key(keypair.private) .apply(&iface)?; - fetch(&iface, false, hosts_file)?; - - if Confirm::with_theme(&*prompts::THEME) - .with_prompt(&format!( - "Delete invitation file \"{}\" now? (It's no longer needed)", - invite.to_string_lossy().yellow() - )) - .default(true) - .interact()? - { - std::fs::remove_file(invite).with_path(invite)?; - } - - printdoc!( - " - {star} Done! - - {interface} has been {installed}. - - It's recommended to now keep the interface automatically refreshing via systemd: - - {systemctl_enable}{interface} - - By default, innernet will write to your /etc/hosts file for peer name - resolution. To disable this behavior, use the --no-write-hosts or --write-hosts [PATH] - options. - - See the manpage or innernet GitHub repo for more detailed instruction on managing your - interface and network. Have fun! - - ", - star = "[*]".dimmed(), - interface = iface.to_string().yellow(), - installed = "installed".green(), - systemctl_enable = "systemctl enable --now innernet@".yellow(), - ); - Ok(()) } diff --git a/client/src/util.rs b/client/src/util.rs index e4ae6eb..75c7c2b 100644 --- a/client/src/util.rs +++ b/client/src/util.rs @@ -2,6 +2,7 @@ use crate::{ClientError, Error}; use colored::*; use serde::{de::DeserializeOwned, Serialize}; use shared::{interface_config::ServerInfo, INNERNET_PUBKEY_HEADER}; +use ureq::{Agent, AgentBuilder}; use std::time::Duration; pub fn human_duration(duration: Duration) -> String { @@ -48,12 +49,17 @@ pub fn human_size(bytes: u64) -> String { } pub struct Api<'a> { + agent: Agent, server: &'a ServerInfo, } impl<'a> Api<'a> { pub fn new(server: &'a ServerInfo) -> Self { - Self { server } + let agent = AgentBuilder::new() + .timeout(Duration::from_secs(5)) + .redirects(0) + .build(); + Self { agent, server } } pub fn http(&self, verb: &str, endpoint: &str) -> Result { @@ -75,7 +81,7 @@ impl<'a> Api<'a> { endpoint: &str, form: Option, ) -> Result { - let request = ureq::request( + let request = self.agent.request( verb, &format!("http://{}/v1{}", self.server.internal_endpoint, endpoint), )