From f31718d8cbcc691568f732b21b164d4b4b03d897 Mon Sep 17 00:00:00 2001 From: Hristo Venev Date: Sat, 28 Mar 2020 11:54:11 +0200 Subject: Use `wg addconf'. --- src/fileutil.rs | 5 +++ src/manager/mod.rs | 4 +- src/wg.rs | 114 +++++++++++++++++++++++++++++++---------------------- 3 files changed, 73 insertions(+), 50 deletions(-) diff --git a/src/fileutil.rs b/src/fileutil.rs index 941eb08..67c48e2 100644 --- a/src/fileutil.rs +++ b/src/fileutil.rs @@ -24,6 +24,11 @@ impl Drop for Temp { } impl Temp { + #[inline] + pub fn path(&self) -> &Path { + &self.path + } + #[inline] pub fn leave(mut self) -> PathBuf { mem::replace(&mut self.path, PathBuf::new()) diff --git a/src/manager/mod.rs b/src/manager/mod.rs index 646349a..0425e9d 100644 --- a/src/manager/mod.rs +++ b/src/manager/mod.rs @@ -35,11 +35,11 @@ impl Manager { io::Error::new(io::ErrorKind::InvalidInput, "runtime directory required") })?; - let mut state_path = runtime_directory; + let mut state_path = runtime_directory.clone(); state_path.push("state.json"); let mut m = Self { - dev: wg::Device::open(ifname)?, + dev: wg::Device::open(ifname, runtime_directory)?, global_config: c.global, sources: vec![], current: model::Config::empty(), diff --git a/src/wg.rs b/src/wg.rs index 766f18f..190c626 100644 --- a/src/wg.rs +++ b/src/wg.rs @@ -2,24 +2,26 @@ // // Copyright 2019 Hristo Venev -use crate::model; +use crate::{fileutil, model}; use std::ffi::{OsStr, OsString}; +use std::path::PathBuf; use std::process::{Command, Stdio}; use std::{env, fmt, io}; pub struct Device { ifname: OsString, + tmpdir: PathBuf, } impl Device { #[inline] - pub fn open(ifname: OsString) -> io::Result { - let dev = Self { ifname }; + pub fn open(ifname: OsString, tmpdir: PathBuf) -> io::Result { + let dev = Self { ifname, tmpdir }; let _ = dev.get_public_key()?; Ok(dev) } - pub fn wg_command() -> Command { + fn wg_command() -> Command { let wg = match env::var_os("WG") { None => OsString::new(), Some(v) => v, @@ -54,12 +56,7 @@ impl Device { } pub fn apply_diff(&mut self, old: &model::Config, new: &model::Config) -> io::Result<()> { - let mut proc = Self::wg_command(); - proc.stdin(Stdio::piped()); - proc.stdout(Stdio::null()); - proc.arg("set"); - proc.arg(&self.ifname); - let mut stdin = String::new(); + let mut config = String::new(); for (pubkey, conf) in &new.peers { let old_endpoint; @@ -75,70 +72,91 @@ impl Device { old_psk = None; } - proc.arg("peer"); - proc.arg(pubkey.to_string()); - - proc.arg("persistent-keepalive"); - proc.arg(conf.keepalive.to_string()); + use fmt::Write; + write!( + config, + "[Peer]\nPublicKey={}\nPersistentKeepalive={}\nAllowedIPs", + pubkey, conf.keepalive + ) + .unwrap(); + let mut delim = '='; + for ip in &conf.ipv4 { + config.push(delim); + delim = ','; + write!(config, "{}", ip).unwrap(); + } + for ip in &conf.ipv6 { + config.push(delim); + delim = ','; + write!(config, "{}", ip).unwrap(); + } + config.push('\n'); if old_endpoint != conf.endpoint { if let Some(ref endpoint) = conf.endpoint { - proc.arg("endpoint"); - proc.arg(endpoint.to_string()); + write!(config, "Endpoint={}\n", endpoint).unwrap(); } } if old_psk != conf.psk.as_ref() { - proc.arg("preshared-key"); - proc.arg("-"); + config.push_str("PresharedKey="); if let Some(psk) = conf.psk.as_ref() { - use fmt::Write; - writeln!(stdin, "{}", psk).unwrap(); + writeln!(config, "{}", psk).unwrap(); + config.push('\n'); } else { - stdin.push('\n'); + config.push_str("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA=\n"); } } + } - let mut ips = String::new(); - { - use std::fmt::Write; - for ip in &conf.ipv4 { - if !ips.is_empty() { - ips.push(','); - } - write!(ips, "{}", ip).unwrap(); - } - for ip in &conf.ipv6 { - if !ips.is_empty() { - ips.push(','); - } - write!(ips, "{}", ip).unwrap(); - } + { + let mut config_file = fileutil::Writer::new_in(&self.tmpdir)?; + io::Write::write_all(config_file.file(), config.as_bytes())?; + let config_file = config_file.done(); + + let mut proc = Self::wg_command(); + proc.stdin(Stdio::null()); + proc.stdout(Stdio::null()); + proc.arg("addconf"); + proc.arg(&self.ifname); + proc.arg(config_file.path()); + + let r = proc.status()?; + if !r.success() { + return Err(io::Error::new( + io::ErrorKind::Other, + "`wg setconf' process failed", + )); } - - proc.arg("allowed-ips"); - proc.arg(ips); } + let mut proc = Self::wg_command(); + let mut any_removed = false; + proc.stdin(Stdio::null()); + proc.stdout(Stdio::null()); + proc.arg("set"); + proc.arg(&self.ifname); + for pubkey in old.peers.keys() { if new.peers.contains_key(pubkey) { continue; } + any_removed = true; proc.arg("peer"); proc.arg(pubkey.to_string()); proc.arg("remove"); } - let mut proc = proc.spawn()?; - { - use io::Write; - proc.stdin.as_mut().unwrap().write_all(stdin.as_bytes())?; + if any_removed { + let r = proc.status()?; + if !r.success() { + return Err(io::Error::new( + io::ErrorKind::Other, + "`wg set' process failed", + )); + } } - let r = proc.wait()?; - if !r.success() { - return Err(io::Error::new(io::ErrorKind::Other, "child process failed")); - } Ok(()) } } -- cgit