// Copyright 2019 Hristo Venev // // See COPYING. use crate::model; use std::ffi::{OsStr, OsString}; use std::process::{Command, Stdio}; use std::{env, io}; pub struct Device { ifname: String, } impl Device { #[inline] pub fn new(ifname: String) -> io::Result { Ok(Device { ifname }) } pub fn wg_command() -> Command { let wg = match env::var_os("WG") { None => OsString::new(), Some(v) => v, }; Command::new(if wg.is_empty() { OsStr::new("wg") } else { wg.as_os_str() }) } pub fn get_public_key(&self) -> io::Result { let mut proc = Device::wg_command(); proc.stdin(Stdio::null()); proc.stdout(Stdio::piped()); proc.arg("show"); proc.arg(&self.ifname); proc.arg("public-key"); let r = proc.output()?; if !r.status.success() { return Err(io::Error::new(io::ErrorKind::Other, "Child process failed")); } let mut out = r.stdout; if out.ends_with(b"\n") { out.remove(out.len() - 1); } model::Key::from_bytes(&out) .map_err(|_| io::Error::new(io::ErrorKind::InvalidData, "Invalid public key")) } pub fn apply_diff(&mut self, old: &model::Config, new: &model::Config) -> io::Result<()> { let mut proc = Device::wg_command(); proc.stdin(Stdio::piped()); proc.arg("set"); proc.arg(&self.ifname); let mut psks = String::new(); for (pubkey, conf) in new.peers.iter() { let old_endpoint; if let Some(old_peer) = old.peers.get(pubkey) { if *old_peer == *conf { continue; } old_endpoint = old_peer.endpoint.clone(); } else { old_endpoint = None; } proc.arg("peer"); proc.arg(format!("{}", pubkey)); if old_endpoint != conf.endpoint { if let Some(ref endpoint) = conf.endpoint { proc.arg("endpoint"); proc.arg(format!("{}", endpoint)); } } if let Some(psk) = &conf.psk { proc.arg("preshared-key"); proc.arg("/dev/stdin"); { use std::fmt::Write; writeln!(&mut psks, "{}", psk).unwrap(); } } let mut ips = String::new(); { use std::fmt::Write; for ip in conf.ipv4.iter() { if !ips.is_empty() { ips.push(','); } write!(ips, "{}", ip).unwrap(); } for ip in conf.ipv6.iter() { if !ips.is_empty() { ips.push(','); } write!(ips, "{}", ip).unwrap(); } } proc.arg("allowed-ips"); proc.arg(ips); } for pubkey in old.peers.keys() { if new.peers.contains_key(pubkey) { continue; } proc.arg("peer"); proc.arg(format!("{}", pubkey)); proc.arg("remove"); } let mut proc = proc.spawn()?; { use std::io::Write; let stdin = proc.stdin.as_mut().unwrap(); write!(stdin, "{}", psks)?; } let r = proc.wait()?; if !r.success() { return Err(io::Error::new(io::ErrorKind::Other, "Child process failed")); } Ok(()) } }