diff options
-rw-r--r-- | Cargo.toml | 1 | ||||
-rw-r--r-- | src/bin.rs | 59 | ||||
-rw-r--r-- | src/config.rs | 19 | ||||
-rw-r--r-- | src/main.rs | 293 | ||||
-rw-r--r-- | src/manager.rs | 332 | ||||
-rw-r--r-- | src/model.rs | 9 | ||||
-rw-r--r-- | src/model/ip.rs (renamed from src/ip.rs) | 12 | ||||
-rw-r--r-- | src/proto.rs | 35 | ||||
-rw-r--r-- | src/wg.rs | 8 |
9 files changed, 459 insertions, 309 deletions
@@ -15,3 +15,4 @@ toml = { version = "0.5" } [profile.release] panic = "abort" +lto = true diff --git a/src/bin.rs b/src/bin.rs deleted file mode 100644 index bd1cf87..0000000 --- a/src/bin.rs +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright 2019 Hristo Venev -// -// See COPYING. - -#[inline] -pub fn i64_to_be(v: i64) -> [u8; 8] { - u64_to_be(v as u64) -} - -#[inline] -pub fn i64_from_be(v: [u8; 8]) -> i64 { - u64_from_be(v) as i64 -} - -#[inline] -pub fn u64_to_be(v: u64) -> [u8; 8] { - [ - (v >> 56) as u8, - (v >> 48) as u8, - (v >> 40) as u8, - (v >> 32) as u8, - (v >> 24) as u8, - (v >> 16) as u8, - (v >> 8) as u8, - v as u8, - ] -} - -#[inline] -pub fn u64_from_be(v: [u8; 8]) -> u64 { - (u64::from(v[0]) << 56) - | (u64::from(v[1]) << 48) - | (u64::from(v[2]) << 40) - | (u64::from(v[3]) << 32) - | (u64::from(v[4]) << 24) - | (u64::from(v[5]) << 16) - | (u64::from(v[6]) << 8) - | u64::from(v[7]) -} - -#[inline] -pub fn u32_to_be(v: u32) -> [u8; 4] { - [(v >> 24) as u8, (v >> 16) as u8, (v >> 8) as u8, v as u8] -} - -#[inline] -pub fn u32_from_be(v: [u8; 4]) -> u32 { - (u32::from(v[0]) << 24) | (u32::from(v[1]) << 16) | (u32::from(v[2]) << 8) | u32::from(v[3]) -} - -#[inline] -pub fn u16_to_be(v: u16) -> [u8; 2] { - [(v >> 8) as u8, v as u8] -} - -#[inline] -pub fn u16_from_be(v: [u8; 2]) -> u16 { - (u16::from(v[0]) << 8) | u16::from(v[1]) -} diff --git a/src/config.rs b/src/config.rs index e8dcb05..6eb3761 100644 --- a/src/config.rs +++ b/src/config.rs @@ -2,8 +2,9 @@ // // See COPYING. -use crate::ip::{Ipv4Set, Ipv6Set}; -use crate::model::Key; +use crate::model::{Ipv4Set, Ipv6Set, Key}; +use std::path::PathBuf; +use std::collections::HashMap; use serde_derive; #[serde(deny_unknown_fields)] @@ -13,6 +14,8 @@ pub struct Source { pub psk: Option<Key>, pub ipv4: Ipv4Set, pub ipv6: Ipv6Set, + #[serde(default)] + pub required: bool, } #[serde(deny_unknown_fields)] @@ -20,7 +23,7 @@ pub struct Source { pub struct PeerConfig { #[serde(default = "default_min_keepalive")] pub min_keepalive: u32, - #[serde(default = "default_max_keepalive")] + #[serde(default)] pub max_keepalive: u32, } @@ -35,6 +38,9 @@ pub struct UpdateConfig { #[serde(deny_unknown_fields)] #[derive(serde_derive::Serialize, serde_derive::Deserialize, Clone, Debug)] pub struct Config { + pub cache_directory: Option<PathBuf>, + pub runtime_directory: Option<PathBuf>, + #[serde(flatten)] pub peer_config: PeerConfig, @@ -42,7 +48,7 @@ pub struct Config { pub update_config: UpdateConfig, #[serde(rename = "source")] - pub sources: Vec<Source>, + pub sources: HashMap<String, Source>, } impl PeerConfig { @@ -63,11 +69,6 @@ fn default_min_keepalive() -> u32 { } #[inline] -fn default_max_keepalive() -> u32 { - 0 -} - -#[inline] fn default_refresh_sec() -> u32 { 1200 } diff --git a/src/main.rs b/src/main.rs index 9ad0f9b..dfadaf4 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,212 +5,19 @@ #[macro_use] extern crate arrayref; -use std::io; -use std::time::{Duration, Instant, SystemTime}; +use std::{env, fs, io, process, thread}; +use std::time::Instant; +use std::ffi::{OsStr, OsString}; +use toml; -mod bin; mod builder; -mod config; -mod ip; mod model; +mod config; mod proto; mod wg; +mod manager; -struct Source { - config: config::Source, - data: Option<proto::Source>, - next_update: Instant, - backoff: Option<Duration>, -} - -impl Source { - #[inline] - fn new(config: config::Source) -> Source { - Source { - config, - data: None, - next_update: Instant::now(), - backoff: None, - } - } -} - -pub struct Device { - dev: wg::Device, - peer_config: config::PeerConfig, - update_config: config::UpdateConfig, - sources: Vec<Source>, - current: model::Config, -} - -impl Device { - pub fn new(ifname: String, c: config::Config) -> io::Result<Device> { - let dev = wg::Device::new(ifname)?; - let _ = dev.get_public_key()?; - - Ok(Device { - dev, - peer_config: c.peer_config, - update_config: c.update_config, - sources: c.sources.into_iter().map(Source::new).collect(), - current: model::Config::default(), - }) - } - - fn make_config( - &self, - public_key: model::Key, - ts: SystemTime, - ) -> (model::Config, Vec<builder::ConfigError>, SystemTime) { - let mut t_cfg = ts + Duration::from_secs(1 << 30); - let mut sources: Vec<(&Source, &proto::SourceConfig)> = vec![]; - for src in self.sources.iter() { - if let Some(ref data) = src.data { - let sc = data - .next - .as_ref() - .and_then(|next| { - if ts >= next.update_at { - Some(&next.config) - } else { - t_cfg = t_cfg.min(next.update_at); - None - } - }) - .unwrap_or(&data.config); - sources.push((src, sc)); - } - } - - let mut cfg = builder::ConfigBuilder::new(public_key, &self.peer_config); - - for (src, sc) in sources.iter() { - for peer in sc.servers.iter() { - cfg.add_server(&src.config, peer); - } - } - - for (src, sc) in sources.iter() { - for peer in sc.road_warriors.iter() { - cfg.add_road_warrior(&src.config, peer); - } - } - - let (cfg, errs) = cfg.build(); - (cfg, errs, t_cfg) - } - - pub fn update(&mut self) -> io::Result<Instant> { - let refresh = Duration::from_secs(u64::from(self.update_config.refresh_sec)); - let mut now = Instant::now(); - let mut t_refresh = now + refresh; - - for src in self.sources.iter_mut() { - if now < src.next_update { - t_refresh = t_refresh.min(src.next_update); - continue; - } - - let r = fetch_source(&src.config.url); - now = Instant::now(); - let r = match r { - Ok(r) => { - eprintln!("<6>Updated [{}]", &src.config.url); - src.data = Some(r); - src.backoff = None; - src.next_update = now + refresh; - continue; - } - Err(r) => r, - }; - - let b = src.backoff.unwrap_or(if src.data.is_some() { - refresh / 3 - } else { - Duration::from_secs(10).min(refresh / 10) - }); - src.next_update = now + b; - t_refresh = t_refresh.min(src.next_update); - - eprintln!("<3>Failed to update [{}], retrying after {:.1?}: {}", &src.config.url, b, &r); - - src.backoff = Some((b + b / 3).min(refresh)); - } - - let now = Instant::now(); - let sysnow = SystemTime::now(); - let public_key = self.dev.get_public_key()?; - let (config, errors, t_cfg) = self.make_config(public_key, sysnow); - let time_to_cfg = t_cfg - .duration_since(sysnow) - .unwrap_or(Duration::from_secs(0)); - let t_cfg = now + time_to_cfg; - - if config != self.current { - eprintln!("<5>Applying configuration update"); - for err in errors.iter() { - eprintln!("<{}>{}", if err.important { '4' } else { '5' }, err); - } - self.dev.apply_diff(&self.current, &config)?; - self.current = config; - } - - Ok(if t_cfg < t_refresh { - eprintln!("<6>Next configuration update after {:.1?}", time_to_cfg); - t_cfg - } else if t_refresh > now { - t_refresh - } else { - eprintln!("<4>Next refresh immediately?"); - now - }) - } -} - -fn fetch_source(url: &str) -> io::Result<proto::Source> { - use std::env; - use std::ffi::{OsStr, OsString}; - use std::process::{Command, Stdio}; - - let curl = match env::var_os("CURL") { - None => OsString::new(), - Some(v) => v, - }; - let mut proc = Command::new(if curl.is_empty() { - OsStr::new("curl") - } else { - curl.as_os_str() - }); - - proc.stdin(Stdio::null()); - proc.stdout(Stdio::piped()); - proc.stderr(Stdio::piped()); - proc.arg("-gsSfL"); - proc.arg("--fail-early"); - proc.arg("--max-time"); - proc.arg("10"); - proc.arg("--max-filesize"); - proc.arg("1M"); - proc.arg("--"); - proc.arg(url); - - let out = proc.output()?; - - if !out.status.success() { - let msg = String::from_utf8_lossy(&out.stderr); - let msg = msg.replace('\n', "; "); - return Err(io::Error::new(io::ErrorKind::Other, msg)); - } - - let mut de = serde_json::Deserializer::from_slice(&out.stdout); - let r = serde::Deserialize::deserialize(&mut de)?; - Ok(r) -} - -fn load_config(path: &str) -> io::Result<config::Config> { - use std::fs; - use toml; - +fn load_config(path: &OsStr) -> io::Result<config::Config> { let mut data = String::new(); { use io::Read; @@ -222,24 +29,36 @@ fn load_config(path: &str) -> io::Result<config::Config> { .map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e)) } -fn main() { - use std::{env, process, thread}; +fn usage(argv0: &str) -> i32 { + eprintln!("<1>Invalid arguments. See `{} --help` for more information", argv0); + 1 +} + +fn help(argv0: &str) -> i32 { + println!("Usage:"); + println!(" {} IFNAME CONFIG - run daemon on iterface", argv0); + println!(" {} --check-source PATH - validate source JSON", argv0); + 1 +} - let args: Vec<String> = env::args().collect(); - if args.len() != 3 { - let arg0 = if !args.is_empty() { &args[0] } else { "wgconf" }; - eprintln!("<1>Usage:"); - eprintln!("<1> {} IFNAME CONFIG", arg0); - process::exit(1); +fn maybe_get_var(out: &mut Option<impl From<OsString>>, var: impl AsRef<OsStr>) { + let var = var.as_ref(); + if let Some(s) = env::var_os(var) { + env::remove_var(var); + *out = Some(s.into()); } +} +fn run_daemon(argv0: String, args: Vec<OsString>) -> i32 { + if args.len() != 2 { + return usage(&argv0); + } let mut args = args.into_iter(); - let _ = args.next().unwrap(); let ifname = args.next().unwrap(); let config_path = args.next().unwrap(); assert!(args.next().is_none()); - let config = match load_config(&config_path) { + let mut config = match load_config(&config_path) { Ok(c) => c, Err(e) => { eprintln!("<1>Failed to load config: {}", e); @@ -247,8 +66,11 @@ fn main() { } }; - let mut dev = match Device::new(ifname, config) { - Ok(dev) => dev, + maybe_get_var(&mut config.cache_directory, "CACHE_DIRECTORY"); + maybe_get_var(&mut config.runtime_directory, "RUNTIME_DIRECTORY"); + + let mut m = match manager::Manager::new(ifname, config) { + Ok(m) => m, Err(e) => { eprintln!("<1>Failed to open device: {}", e); process::exit(1); @@ -256,7 +78,7 @@ fn main() { }; loop { - let tm = match dev.update() { + let tm = match m.update() { Ok(t) => t, Err(e) => { eprintln!("<1>{}", e); @@ -270,3 +92,48 @@ fn main() { } } } + +fn run_check_source(argv0: String, args: Vec<OsString>) -> i32 { + if args.len() != 1 { + usage(&argv0); + } + let mut args = args.into_iter(); + let path = args.next().unwrap(); + assert!(args.next().is_none()); + + match manager::load_source(&path) { + Ok(_) => { + println!("OK"); + 0 + } + Err(e) => { + println!("{}", e); + 1 + } + } +} + +fn main() -> () { + let mut iter_args = env::args_os(); + let argv0 = iter_args.next().unwrap().to_string_lossy().into_owned(); + + let mut args = Vec::new(); + let mut run: for<'a> fn(String, Vec<OsString>) -> i32 = run_daemon; + let mut parse_args = true; + for arg in iter_args { + if !parse_args || !arg.to_string_lossy().starts_with('-') { + args.push(arg); + } else if arg == "--" { + parse_args = false; + } else if arg == "-h" || arg == "--help" { + process::exit(help(&argv0)); + } else if arg == "--check-source" { + run = run_check_source; + parse_args = false; + } else { + usage(&argv0); + } + } + + process::exit(run(argv0, args)); +} diff --git a/src/manager.rs b/src/manager.rs new file mode 100644 index 0000000..20d7f50 --- /dev/null +++ b/src/manager.rs @@ -0,0 +1,332 @@ +// Copyright 2019 Hristo Venev +// +// See COPYING. + +use std::{io, fs}; +use crate::{builder, config, model, proto, wg}; +use std::time::{Duration, Instant, SystemTime}; +use std::ffi::{OsStr, OsString}; +use std::path::PathBuf; + +struct Source { + name: String, + config: config::Source, + data: proto::Source, + next_update: Instant, + backoff: Option<Duration>, +} + +struct Updater { + config: config::UpdateConfig, + cache_directory: Option<PathBuf>, +} + +impl Updater { + fn cache_path(&self, s: &Source) -> Option<PathBuf> { + if let Some(ref dir) = self.cache_directory { + let mut p = dir.clone(); + p.push(&s.name); + Some(p) + } else { + None + } + } + + fn cache_update(&self, src: &Source) -> io::Result<bool> { + let path = match self.cache_path(src) { + Some(path) => path, + None => { + return Ok(false); + } + }; + + let mut tmp_path = OsString::from(path.clone()); + tmp_path.push(".tmp"); + let tmp_path = PathBuf::from(tmp_path); + + let data = serde_json::to_vec(&src.data).unwrap(); + + let mut file = fs::File::create(&tmp_path)?; + match io::Write::write_all(&mut file, &data) + .and_then(|_| file.sync_data()) + .and_then(|_| fs::rename(&tmp_path, &path)) + { + Ok(()) => {} + Err(e) => { + fs::remove_file(&tmp_path).unwrap_or_else(|e2| { + eprintln!("<3>Failed to clean up [{}]: {}", tmp_path.display(), e2); + }); + return Err(e); + } + } + + Ok(true) + } + + fn cache_load(&self, src: &mut Source) -> bool { + let path = match self.cache_path(src) { + Some(path) => path, + None => { + return false; + } + }; + + let mut file = match fs::File::open(&path) { + Ok(file) => file, + Err(_) => { + return false; + } + }; + + let mut data = Vec::new(); + match io::Read::read_to_end(&mut file, &mut data) { + Ok(_) => {} + Err(e) => { + eprintln!("<3>Failed to read [{}] from cache: {}", src.config.url, e); + return false; + } + }; + + let mut de = serde_json::Deserializer::from_slice(&data); + src.data = match serde::Deserialize::deserialize(&mut de) { + Ok(r) => r, + Err(e) => { + eprintln!("<3>Failed to load [{}] from cache: {}", src.config.url, e); + return false; + } + }; + + true + } + + fn update(&self, src: &mut Source) -> (bool, Instant) { + let refresh = Duration::from_secs(u64::from(self.config.refresh_sec)); + + let r = fetch_source(&src.config.url); + let now = Instant::now(); + let r = match r { + Ok(r) => { + eprintln!("<6>Updated [{}]", &src.config.url); + src.data = r; + src.backoff = None; + src.next_update = now + refresh; + match self.cache_update(src) { + Ok(_) => {} + Err(e) => { + eprintln!("<4>Failed to cache [{}]: {}", &src.config.url, e); + } + } + return (true, now); + } + Err(r) => r, + }; + + let b = src.backoff.unwrap_or_else(|| Duration::from_secs(10).min(refresh / 10)); + src.next_update = now + b; + src.backoff = Some((b + b / 3).min(refresh / 3)); + eprintln!("<3>Failed to update [{}], retrying after {:.1?}: {}", &src.config.url, b, &r); + (false, now) + } + +} + +pub struct Manager { + dev: wg::Device, + peer_config: config::PeerConfig, + sources: Vec<Source>, + current: model::Config, + updater: Updater, +} + +impl Manager { + pub fn new(ifname: OsString, c: config::Config) -> io::Result<Manager> { + let mut m = Manager { + dev: wg::Device::new(ifname)?, + peer_config: c.peer_config, + sources: vec![], + current: model::Config::default(), + updater: Updater { + config: c.update_config, + cache_directory: c.cache_directory, + }, + }; + + for (name, cfg) in c.sources.into_iter() { + m.add_source(name, cfg)?; + } + + Ok(m) + } + + fn add_source(&mut self, name: String, config: config::Source) -> io::Result<()> { + let mut s = Source { + name, + config, + data: proto::Source::empty(), + next_update: Instant::now(), + backoff: None, + }; + + self.init_source(&mut s)?; + self.sources.push(s); + Ok(()) + } + + fn init_source(&mut self, s: &mut Source) -> io::Result<()> { + if self.updater.update(s).0 { + return Ok(()); + } + if self.updater.cache_load(s) { + return Ok(()); + } + if !s.config.required { + return Ok(()); + } + if self.updater.update(s).0 { + return Ok(()); + } + if self.updater.update(s).0 { + return Ok(()); + } + Err(io::Error::new(io::ErrorKind::Other, format!("Failed to update required source [{}]", &s.config.url))) + } + + fn make_config( + &self, + public_key: model::Key, + ts: SystemTime, + ) -> (model::Config, Vec<builder::ConfigError>, SystemTime) { + let mut t_cfg = ts + Duration::from_secs(1 << 30); + let mut sources: Vec<(&Source, &proto::SourceConfig)> = vec![]; + for src in self.sources.iter() { + let sc = src.data.next + .as_ref() + .and_then(|next| { + if ts >= next.update_at { + Some(&next.config) + } else { + t_cfg = t_cfg.min(next.update_at); + None + } + }) + .unwrap_or(&src.data.config); + sources.push((src, sc)); + } + + let mut cfg = builder::ConfigBuilder::new(public_key, &self.peer_config); + + for (src, sc) in sources.iter() { + for peer in sc.servers.iter() { + cfg.add_server(&src.config, peer); + } + } + + for (src, sc) in sources.iter() { + for peer in sc.road_warriors.iter() { + cfg.add_road_warrior(&src.config, peer); + } + } + + let (cfg, errs) = cfg.build(); + (cfg, errs, t_cfg) + } + + fn refresh(&mut self) -> io::Result<Instant> { + let refresh = Duration::from_secs(u64::from(self.updater.config.refresh_sec)); + let mut now = Instant::now(); + let mut t_refresh = now + refresh; + + for src in self.sources.iter_mut() { + if now >= src.next_update { + now = self.updater.update(src).1; + } + t_refresh = t_refresh.min(src.next_update); + } + + Ok(t_refresh) + } + + pub fn update(&mut self) -> io::Result<Instant> { + let t_refresh = self.refresh()?; + + let public_key = self.dev.get_public_key()?; + let now = Instant::now(); + let sysnow = SystemTime::now(); + let (config, errors, t_cfg) = self.make_config(public_key, sysnow); + let time_to_cfg = t_cfg + .duration_since(sysnow) + .unwrap_or(Duration::from_secs(0)); + let t_cfg = now + time_to_cfg; + + if config != self.current { + eprintln!("<5>Applying configuration update"); + for err in errors.iter() { + eprintln!("<{}>{}", if err.important { '4' } else { '5' }, err); + } + self.dev.apply_diff(&self.current, &config)?; + self.current = config; + } + + Ok(if t_cfg < t_refresh { + eprintln!("<6>Next configuration update after {:.1?}", time_to_cfg); + t_cfg + } else if t_refresh > now { + t_refresh + } else { + eprintln!("<4>Next refresh immediately?"); + now + }) + } +} + +pub fn fetch_source(url: &str) -> io::Result<proto::Source> { + use std::env; + use std::process::{Command, Stdio}; + + let curl = match env::var_os("CURL") { + None => OsString::new(), + Some(v) => v, + }; + let mut proc = Command::new(if curl.is_empty() { + OsStr::new("curl") + } else { + curl.as_os_str() + }); + + proc.stdin(Stdio::null()); + proc.stdout(Stdio::piped()); + proc.stderr(Stdio::piped()); + proc.arg("-gsSfL"); + proc.arg("--fail-early"); + proc.arg("--max-time"); + proc.arg("10"); + proc.arg("--max-filesize"); + proc.arg("1M"); + proc.arg("--"); + proc.arg(url); + + let out = proc.output()?; + + if !out.status.success() { + let msg = String::from_utf8_lossy(&out.stderr); + let msg = msg.replace('\n', "; "); + return Err(io::Error::new(io::ErrorKind::Other, msg)); + } + + let mut de = serde_json::Deserializer::from_slice(&out.stdout); + let r = serde::Deserialize::deserialize(&mut de)?; + Ok(r) +} + +pub fn load_source(path: &OsStr) -> io::Result<proto::Source> { + let mut data = Vec::new(); + { + use std::io::Read; + let mut f = fs::File::open(&path)?; + f.read_to_end(&mut data)?; + } + + let mut de = serde_json::Deserializer::from_slice(&data); + let r = serde::Deserialize::deserialize(&mut de)?; + Ok(r) +} diff --git a/src/model.rs b/src/model.rs index f8b76b5..bc800b2 100644 --- a/src/model.rs +++ b/src/model.rs @@ -2,13 +2,14 @@ // // See COPYING. -use crate::bin; -use crate::ip::{Ipv4Addr, Ipv4Net, Ipv6Addr, Ipv6Net, NetParseError}; use base64; use std::collections::HashMap; use std::fmt; use std::str::FromStr; +mod ip; +pub use ip::*; + pub type KeyParseError = base64::DecodeError; #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] @@ -142,7 +143,7 @@ impl serde::Serialize for Endpoint { let mut buf = [0u8; 16 + 2]; let (buf_addr, buf_port) = mut_array_refs![&mut buf, 16, 2]; *buf_addr = self.address.octets(); - *buf_port = crate::bin::u16_to_be(self.port); + *buf_port = self.port.to_be_bytes(); ser.serialize_bytes(&buf) } } @@ -171,7 +172,7 @@ impl<'de> serde::Deserialize<'de> for Endpoint { let (buf_addr, buf_port) = array_refs![&buf, 16, 2]; Ok(Endpoint { address: (*buf_addr).into(), - port: bin::u16_from_be(*buf_port), + port: u16::from_be_bytes(*buf_port), }) } } diff --git a/src/ip.rs b/src/model/ip.rs index d2ad17e..0ada314 100644 --- a/src/ip.rs +++ b/src/model/ip.rs @@ -136,7 +136,7 @@ macro_rules! per_proto { } #[inline] - fn siblings(a: &$nett, b: &$nett) -> bool { + fn siblings(a: $nett, b: $nett) -> bool { let pfx = a.prefix_len; if b.prefix_len != pfx || pfx == 0 { return false; @@ -162,9 +162,9 @@ macro_rules! per_proto { j += 1; } loop { - if j < self.nets.len() && Self::siblings(&net, &self.nets[j]) { + if j < self.nets.len() && Self::siblings(net, self.nets[j]) { j += 1; - } else if i != 0 && Self::siblings(&self.nets[i - 1], &net) { + } else if i != 0 && Self::siblings(self.nets[i - 1], net) { net = self.nets[i - 1]; i -= 1; } else { @@ -249,7 +249,7 @@ macro_rules! per_proto { net = s.nets[i - 1]; i -= 1; } - while i != 0 && Self::siblings(&s.nets[i - 1], &net) { + while i != 0 && Self::siblings(s.nets[i - 1], net) { net = s.nets[i - 1]; net.prefix_len -= 1; i -= 1; @@ -296,7 +296,7 @@ per_proto!(Ipv4Net(Ipv4Addr; "IPv4 network"); u32(4); Ipv4Set); per_proto!(Ipv6Net(Ipv6Addr; "IPv6 network"); u128(16); Ipv6Set); impl Ipv4Net { - pub fn is_valid(&self) -> bool { + pub fn is_valid(self) -> bool { let pfx = self.prefix_len; if pfx > 32 { return false; @@ -310,7 +310,7 @@ impl Ipv4Net { } impl Ipv6Net { - pub fn is_valid(&self) -> bool { + pub fn is_valid(self) -> bool { let pfx = self.prefix_len; if pfx > 128 { return false; diff --git a/src/proto.rs b/src/proto.rs index 86c7eee..9c98fff 100644 --- a/src/proto.rs +++ b/src/proto.rs @@ -2,8 +2,7 @@ // // See COPYING. -use crate::ip::{Ipv4Net, Ipv6Net}; -use crate::model::{Endpoint, Key}; +use crate::model::{Endpoint, Ipv4Net, Ipv6Net, Key}; use serde_derive; use std::time::SystemTime; @@ -11,9 +10,9 @@ use std::time::SystemTime; #[derive(serde_derive::Serialize, serde_derive::Deserialize, Clone, PartialEq, Eq, Debug)] pub struct Peer { pub public_key: Key, - #[serde(default = "Vec::new")] + #[serde(default)] pub ipv4: Vec<Ipv4Net>, - #[serde(default = "Vec::new")] + #[serde(default)] pub ipv6: Vec<Ipv6Net>, } @@ -23,7 +22,7 @@ pub struct Server { #[serde(flatten)] pub peer: Peer, pub endpoint: Endpoint, - #[serde(default = "default_peer_keepalive")] + #[serde(default)] pub keepalive: u32, } @@ -37,9 +36,9 @@ pub struct RoadWarrior { #[derive(serde_derive::Serialize, serde_derive::Deserialize, Clone, PartialEq, Eq, Debug)] pub struct SourceConfig { - #[serde(default = "Vec::new")] + #[serde(default)] pub servers: Vec<Server>, - #[serde(default = "Vec::new")] + #[serde(default)] pub road_warriors: Vec<RoadWarrior>, } @@ -58,13 +57,19 @@ pub struct Source { pub next: Option<SourceNextConfig>, } -#[inline] -fn default_peer_keepalive() -> u32 { - 0 +impl Source { + pub fn empty() -> Source { + Source { + config: SourceConfig { + servers: vec![], + road_warriors: vec![], + }, + next: None, + } + } } mod serde_utc { - use crate::bin; use chrono::{DateTime, SecondsFormat, TimeZone, Utc}; use serde::*; use std::fmt; @@ -77,8 +82,8 @@ mod serde_utc { } else { let mut buf = [0u8; 12]; let (buf_secs, buf_nanos) = mut_array_refs![&mut buf, 8, 4]; - *buf_secs = bin::i64_to_be(t.timestamp()); - *buf_nanos = bin::u32_to_be(t.timestamp_subsec_nanos()); + *buf_secs = t.timestamp().to_be_bytes(); + *buf_nanos = t.timestamp_subsec_nanos().to_be_bytes(); ser.serialize_bytes(&buf) } } @@ -103,8 +108,8 @@ mod serde_utc { } else { let mut buf = <[u8; 12]>::deserialize(de)?; let (buf_secs, buf_nanos) = array_refs![&mut buf, 8, 4]; - let secs = bin::i64_from_be(*buf_secs); - let nanos = bin::u32_from_be(*buf_nanos); + let secs = i64::from_be_bytes(*buf_secs); + let nanos = u32::from_be_bytes(*buf_nanos); Ok(Utc.timestamp(secs, nanos).into()) } } @@ -8,13 +8,15 @@ use std::process::{Command, Stdio}; use std::{env, io}; pub struct Device { - ifname: String, + ifname: OsString, } impl Device { #[inline] - pub fn new(ifname: String) -> io::Result<Self> { - Ok(Device { ifname }) + pub fn new(ifname: OsString) -> io::Result<Self> { + let dev = Device { ifname }; + let _ = dev.get_public_key()?; + Ok(dev) } pub fn wg_command() -> Command { |