diff --git a/wg-event-gen/Cargo.lock b/wg-event-gen/Cargo.lock index f6bcb8d..8eb493a 100644 --- a/wg-event-gen/Cargo.lock +++ b/wg-event-gen/Cargo.lock @@ -48,6 +48,65 @@ dependencies = [ "vec_map 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "darling" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "darling_core 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", + "darling_macro 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "darling_core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "darling_macro" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "darling_core 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "derive_builder" +version = "0.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "darling 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_builder_core 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "derive_builder_core" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "darling 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "heck" version = "0.3.1" @@ -61,6 +120,11 @@ name = "hex" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "ident_case" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "libc" version = "0.2.47" @@ -68,7 +132,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "proc-macro2" -version = "0.4.25" +version = "0.4.27" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -79,7 +143,7 @@ name = "quote" version = "0.6.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.25 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -115,7 +179,7 @@ version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "proc-macro2 0.4.25 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "syn 0.15.26 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -125,7 +189,7 @@ name = "syn" version = "0.15.26" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "proc-macro2 0.4.25 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -183,6 +247,7 @@ name = "wg-event-gen" version = "0.1.0" dependencies = [ "base64 0.10.0 (registry+https://github.com/rust-lang/crates.io-index)", + "derive_builder 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "structopt 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", "structopt-derive 0.2.14 (registry+https://github.com/rust-lang/crates.io-index)", @@ -215,10 +280,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum bitflags 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "228047a76f468627ca71776ecdebd732a3423081fcf5125585bcd7c49886ce12" "checksum byteorder 1.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "94f88df23a25417badc922ab0f5716cc1330e87f71ddd9203b3a3ccd9cedf75d" "checksum clap 2.32.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b957d88f4b6a63b9d70d5f454ac8011819c6efa7727858f458ab71c756ce2d3e" +"checksum darling 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "9158d690bc62a3a57c3e45b85e4d50de2008b39345592c64efd79345c7e24be0" +"checksum darling_core 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "d2a368589465391e127e10c9e3a08efc8df66fd49b87dc8524c764bbe7f2ef82" +"checksum darling_macro 0.8.6 (registry+https://github.com/rust-lang/crates.io-index)" = "244e8987bd4e174385240cde20a3657f607fb0797563c28255c353b5819a07b1" +"checksum derive_builder 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "a0ca533e6abb78f9108585535ce2ae0b14c8b4504e138a9a28eaf8ba2b270c1d" +"checksum derive_builder_core 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fb484fe06ba1dc5b82f88aff700191dfc127e02b06b35e302c169706168e2528" +"checksum fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" "checksum heck 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "20564e78d53d2bb135c343b3f47714a56af2061f1c928fdb541dc7b9fdd94205" "checksum hex 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "805026a5d0141ffc30abb3be3173848ad46a1b1664fe632428479619a3644d77" +"checksum ident_case 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "b9e0384b61958566e926dc50660321d12159025e767c18e043daf26b70104c39" "checksum libc 0.2.47 (registry+https://github.com/rust-lang/crates.io-index)" = "48450664a984b25d5b479554c29cc04e3150c97aa4c01da5604a2d4ed9151476" -"checksum proc-macro2 0.4.25 (registry+https://github.com/rust-lang/crates.io-index)" = "d3797b7142c9aa74954e351fc089bbee7958cebbff6bf2815e7ffff0b19f547d" +"checksum proc-macro2 0.4.27 (registry+https://github.com/rust-lang/crates.io-index)" = "4d317f9caece796be1980837fd5cb3dfec5613ebdb04ad0956deea83ce168915" "checksum quote 0.6.10 (registry+https://github.com/rust-lang/crates.io-index)" = "53fa22a1994bd0f9372d7a816207d8a2677ad0325b073f5c5332760f0fb62b5c" "checksum redox_syscall 0.1.50 (registry+https://github.com/rust-lang/crates.io-index)" = "52ee9a534dc1301776eff45b4fa92d2c39b1d8c3d3357e6eb593e0d795506fc2" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" diff --git a/wg-event-gen/Cargo.toml b/wg-event-gen/Cargo.toml index 57918aa..b7f2272 100644 --- a/wg-event-gen/Cargo.toml +++ b/wg-event-gen/Cargo.toml @@ -10,6 +10,7 @@ base64 = "0.10.0" time = "0.1.42" structopt = "0.2.14" structopt-derive = "0.2.14" +derive_builder = "0.7.1" [profile.release] lto = true diff --git a/wg-event-gen/src/controller.rs b/wg-event-gen/src/controller.rs new file mode 100644 index 0000000..ecff9c1 --- /dev/null +++ b/wg-event-gen/src/controller.rs @@ -0,0 +1,68 @@ +use crate::model::WireguardController; +use std::os::unix::net::UnixStream; +use std::path::PathBuf; + +pub struct Userspace(PathBuf); + +impl Userspace { + pub fn new>(path: P) -> Userspace { + Userspace(path.into()) + } +} + +impl WireguardController for Userspace { + fn peers<'a>(&'a mut self) -> Box> + 'a> { + let mut stream = UnixStream::connect(&self.0)?; + stream.write_all(b"get=1\n")?; + + fn build_peer(builder: &mut PeerBuilder, line: String) -> Result<()> { + let line = line?; + let mut iter = line.chars(); + let key = iter.by_ref().take_while(|c| c != &'=').collect::(); + let value = iter.collect::(); + let mut allowed_ips: Vec<(IpAddr, u8)> = Vec::new(); + let mut last_handshake: Option = None; + let update_handshake = |d: Duration| { + last_handshake = last_handshake.map(|c| c + d); + }; + match key.as_ref() { + "public_key" => builder.key(ECCKey::from_base64(value)?), + "private_key" => builder.key(ECCKey::from_base64(value)?), + "endpoint" => builder.endpoint(value::parse::()?), + "last_handshake_time_sec" => { + update_handshake(Duration::from_secs(value::parse::().into())) + } + "last_handshake_time_nsec" => { + update_handshake(Duration::from_nsecs(value::parse::().into())) + } + "persistent_keepalive" => { + builder.keepalive(Duration::from_secs(value::parse::().into())) + } + "allowed_ip" => { + let mut parts = value.split("/").into_iter(); + let ip = match ( + parts.next().and_then(|addr| addr.parse::().ok()), + parts.next().and_then(|mask| mask.parse::().ok()), + ) { + (Some(addr), Some(mask)) => Some((addr, mask)), + (Some(addr), None) if addr.is_ipv6() => Some((addr, 128)), + (Some(addr), None) => Some((addr, 32)), + _ => None, + }; + ips.push(ip); + } + } + builder.allowed_ips(ips); + builder.last_handshake(last_handshake); + } + + let peers = BufReader::new(stream) + .lines() + .scan(PeerBuilder::default(), build_peer); + loop {} + } + + fn update_peer(&mut self, peer: &Peer) -> Result<()> { + loop {} + } +} diff --git a/wg-event-gen/src/main.rs b/wg-event-gen/src/main.rs index 96c4150..9c4ad6f 100644 --- a/wg-event-gen/src/main.rs +++ b/wg-event-gen/src/main.rs @@ -1,258 +1,7 @@ -#[macro_use] -extern crate structopt; +mod controller; -mod gen; -mod listener; -mod opts; - -use listener::*; - -use base64; -use hex; -use opts::Opts; -use std::collections::HashMap; -use std::env; -use std::fmt; -use std::io::prelude::*; -use std::io::{BufRead, BufReader, Error, ErrorKind, Result}; -use std::net::{IpAddr, SocketAddr}; -use std::os::unix::net::UnixStream; -use std::path::PathBuf; -use std::process::exit; -use std::thread; -use std::time::Duration; -use structopt::StructOpt; - -use time; - -pub type KV = (String, String); - -#[derive(Debug, PartialEq, Eq, Hash)] -enum State { - Interface(Vec), - Peer(Vec), -} - -#[derive(Debug, PartialEq, Eq, Hash, Clone)] -pub struct Peer { - public_key: String, - endpoint: Option, - allowed_ips: Vec<(IpAddr, u8)>, - last_handshake: Option, - persistent_keepalive: Option, - traffic: (u64, u64), - parsed: time::Timespec, -} - -impl Peer { - fn from_kv(entries: &Vec) -> Result { - let key = match entries - .iter() - .filter(|(key, _)| key == &"public_key") - .map(|(_, value)| value) - .next() - { - Some(key) => key, - None => return Err(Error::new(ErrorKind::Other, "Peer is missing key")), - }; - Ok(Peer { - public_key: base64::encode(&hex::decode(key).unwrap()), - endpoint: entries - .iter() - .filter(|(key, _)| key == &"endpoint") - .map(|(_, value)| value.parse::().unwrap()) - .next(), - allowed_ips: entries - .iter() - .filter(|(key, _)| key == &"allowed_ip") - .map(|(_, value)| { - let mut parts = value.split("/").into_iter(); - match ( - parts.next().and_then(|addr| addr.parse::().ok()), - parts.next().and_then(|mask| mask.parse::().ok()), - ) { - (Some(addr), Some(mask)) => Some((addr, mask)), - (Some(addr), None) if addr.is_ipv6() => Some((addr, 128)), - (Some(addr), None) => Some((addr, 32)), - _ => None, - } - }) - .filter_map(|net| net) - .collect::>(), - last_handshake: entries - .iter() - .filter_map(|(key, value)| { - let value = || value.parse::().unwrap(); - match key.as_ref() { - "last_handshake_time_sec" if value() != 0 => { - Some(Duration::new(value(), 0)) - } - "last_handshake_time_nsec" if value() != 0 => { - Some(Duration::from_nanos(value())) - } - _ => None, - } - }) - .fold(None, |acc, add| { - if let Some(dur) = acc { - Some(dur + add) - } else { - Some(add) - } - }), - persistent_keepalive: entries - .iter() - .filter(|(key, _)| key == &"persistent_keepalive") - .map(|(_, value)| Duration::from_secs(value.parse::().unwrap())) - .next(), - traffic: (0, 0), - parsed: time::get_time(), - }) - } - - pub fn last_handshake_rel(&self) -> Option { - let time = self.parsed; - Some(Duration::new(time.sec as u64, time.nsec as u32) - self.last_handshake?) - } -} - -impl State { - pub fn kv(&self) -> &Vec { - match self { - State::Interface(kv) => kv, - State::Peer(kv) => kv, - } - } - - fn kv_mut(&mut self) -> &mut Vec { - match self { - State::Interface(kv) => kv, - State::Peer(kv) => kv, - } - } - - pub fn id<'a>(&'a self) -> Option { - self.kv() - .iter() - .filter(|(key, _)| key == &"private_key" || key == &"public_key") - .map(|(_, value)| base64::encode(&hex::decode(&value).unwrap())) - .next() - } - - pub fn push(&mut self, key: String, value: String) { - self.kv_mut().push((key, value)); - } -} - -impl fmt::Display for State { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - for (k, v) in self.kv() { - write!(f, "({:10}= {})", k, v)?; - } - Ok(()) - } -} - -impl fmt::Display for Peer { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{:?}", self) - // write!(f, "peer {}\nshake {} ago\naddr {}\nkeepalive {}\n", self.public_key, self.last_handshake.map(|d|d.to_string()).unwrap_or("-"), self.endpoint.map(|d|d.to_string()).unwrap_or("-"), self.persistent_keepalive.map(|d|d.to_string()).unwrap_or("-")) - } -} - -struct Socket { - pub path: PathBuf, -} - -impl Socket { - pub fn get(&self) -> Result> { - let mut stream = UnixStream::connect(&self.path)?; - stream.write_all(b"get=1\n")?; - let mut state: Vec = vec![]; - let mut cur = State::Interface(Vec::with_capacity(0)); - for line in BufReader::new(stream).lines() { - let line = line?; - let mut iter = line.chars(); - let key = iter.by_ref().take_while(|c| c != &'=').collect::(); - let value = iter.collect::(); - match key.as_ref() { - "errno" if value != "0" => { - Err(Error::new( - ErrorKind::Other, - format!("Socket said error: {}", value), - ))?; - break; - } - "public_key" | "private_key" => { - state.push(cur); - cur = if key == "private_key" { - State::Interface(Vec::with_capacity(3)) - } else { - State::Peer(Vec::with_capacity(5)) - }; - cur.push(key, value); - } - _ => cur.push(key, value), - } - } - Ok(state) - } - - pub fn get_by_id(&self) -> Result> { - let state = self.get()?; - let mut ided = HashMap::new(); - for s in state { - if let Some(id) = s.id() { - ided.insert(id.clone(), s); - } - } - Ok(ided) - } - - pub fn get_peers(&self) -> Result> { - let by_id = self.get_by_id()?; - Ok(by_id - .iter() - .filter_map(|(id, state)| { - Peer::from_kv(state.kv()) - .ok() - .map(|peer| (id.to_owned(), peer)) - }) - .collect()) - } -} +use controller::Userspace; fn main() { - let opts = Opts::from_args(); - - let timeout = Duration::from_secs(opts.timeout); - let interval = Duration::from_secs(opts.poll); - let events = opts.events; - let path = opts.socket; - - let mut listeners: Vec> = vec![Box::new(LogListener)]; - - if let Some(events) = events { - listeners.push(Box::new(ScriptListener::new(events))) - } - - let sock = Socket { path }; - let mut prev_state: Option> = None; - loop { - let state = match sock.get_peers() { - Ok(state) => state, - Err(err) => { - eprintln!("Failed to read from socket: {}", err); - if !opts.ignore_socket_errors { - exit(1); - } - continue; - } - }; - if let Some(prev_state) = prev_state { - gen::gen_events(&state, &prev_state, &listeners, timeout, interval); - } - prev_state = Some(state); - thread::sleep(interval); - } + let controller = Userspace::new(""); } diff --git a/wg-event-gen/src/model.rs b/wg-event-gen/src/model.rs new file mode 100644 index 0000000..2c2390a --- /dev/null +++ b/wg-event-gen/src/model.rs @@ -0,0 +1,47 @@ +#[macro_use] +extern crate structopt; + + + #[macro_use] + extern crate derive_builder; + + +use std::io::Result; +use base64::{decode}; + +pub enum ECCKey{ + PublicKey([u8; 32]), + PrivateKey([u8; 32]) +} + +impl ECCKey { + fn from_base64>(key: I) -> Result { + let key = decode(key.as_ref::())?; + let bytes = [0; 32]; + bytes.copy_from_slice(key); + ECCKey::PublicKey(bytes) + } +} + +struct SharedKey([u8; 32]); + +#[derive(Debug,Builder, PartialEq, Eq, Hash, Clone)] +pub struct Peer { + key: ECCKey, + shared_key: Option, + endpoint: Option, + allowed_ips: Vec<(IpAddr, u8)>, + last_handshake: Option, + persistent_keepalive: Option, + traffic: (u64, u64), + parsed: time::Timespec, +} + + +trait WireguardController { + + fn peers<'a>(&'a mut self) -> Box> + 'a>; + + fn update_peer(&mut self, peer: &Peer) -> Result<()>; + +}