diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/daemon.rs | 153 | ||||
-rw-r--r-- | src/main.rs | 1 |
2 files changed, 126 insertions, 28 deletions
diff --git a/src/daemon.rs b/src/daemon.rs index 1c746fd..220c286 100644 --- a/src/daemon.rs +++ b/src/daemon.rs @@ -1,14 +1,23 @@ use atomic_write_file::AtomicWriteFile; use core::net::SocketAddr; -use defguard_wireguard_rs::{host::Peer, InterfaceConfiguration, WGApi, WireguardInterfaceApi}; -use defguard_wireguard_rs::{key::Key, net::IpAddrMask}; +use dbus::{channel::MatchingReceiver, message::MatchRule}; +use dbus_crossroads::Crossroads; +use defguard_wireguard_rs::{ + host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi, +}; +use futures::future; use log::{info, warn}; use serde::{Deserialize, Serialize}; -use std::collections::BTreeSet; -use std::fs::File; -use std::io::{ErrorKind, Read, Write}; -use std::net::{TcpListener, ToSocketAddrs}; +use std::{ + collections::{HashMap, BTreeSet}, + fs::File, + io::{ErrorKind, Read, Write}, + net::ToSocketAddrs, + sync::Arc, + time::SystemTime, +}; use thiserror::Error; +use tokio::{net::TcpListener, runtime::Builder, sync::RwLock, task}; use xdg::BaseDirectories; use std::str::FromStr; @@ -26,7 +35,13 @@ pub enum DaemonError { Decoding(#[from] serde_json::Error), #[error("{0}")] + IpMaskParse(#[from] defguard_wireguard_rs::net::IpAddrParseError), + + #[error("{0}")] WgInterfaceError(#[from] defguard_wireguard_rs::error::WireguardInterfaceError), + + #[error("{0}")] + DbusError(#[from] dbus::Error), } #[derive(Serialize, Deserialize, Clone)] @@ -38,12 +53,16 @@ enum Endpoint { // subset of defguard_wireguard_rs::host::Peer, with hostname added #[derive(Serialize, Deserialize)] struct PeerConfig { - pubkey: Key, psk: Option<Key>, ips: Vec<(IpAddrMask, Option<String>)>, // if false: the hostnames are kept around for sharing, but we personally do not use them use_hostnames: bool, endpoint: Option<Endpoint>, + + last_changed: SystemTime, + known_to: Vec<usize>, + + mäsch_endpoint: SocketAddr, } fn default_wg_port() -> u16 { @@ -52,7 +71,6 @@ fn default_wg_port() -> u16 { #[derive(Serialize, Deserialize)] struct Network { - name: String, privkey: String, // this really should be a different type, but this is what defguard takes... @@ -61,12 +79,27 @@ struct Network { #[serde(default = "default_wg_port")] listen_port: u16, - peers: Vec<PeerConfig>, + peers: HashMap<Key, PeerConfig>, + + mäsch_port: u16, } #[derive(Serialize, Deserialize, Default)] struct Config { - networks: Vec<Network>, + networks: HashMap<String, Network>, +} + +struct State { + conf: Config, + apis: HashMap<String, WGApi>, +} + +impl Drop for State { + fn drop(&mut self) { + for api in self.apis.values() { + let _ = api.remove_interface(); + } + } } pub fn daemon() -> Result<(), DaemonError> { @@ -80,22 +113,18 @@ pub fn daemon() -> Result<(), DaemonError> { }; info!("read config"); - //let networks = vec![Network { - // name: "kek".to_string(), + //let networks: HashMap<String, Network> = vec![("kek".to_owned(), Network { // privkey: "OK9WQudPVO5rXxcdxdtTzRmJzVu+KuqLMstYsZd8mWE=".to_string(), // address: "1.2.3.4".to_string(), // listen_port: 5221, // peers: vec![PeerConfig { // pubkey: Key::from_str("Osrxi/bRVK+FQit7YMbIgSaOWmRDOZQoh/7ddV4eEE8=").unwrap(), // psk: Some(Key::from_str("wFiG3II9ivYBn+xjLGChC0PjNlbOibZ1K6pmspPD0Hg=").unwrap()), - // use_hostnames: true, + // use_hostnames: false, // endpoint: Some(Endpoint::Domain("alex.69owo.de".to_string(), 12456)), // ips: vec![(IpAddrMask::from_str("5.4.3.2/24").unwrap(), Some("blah.blub".to_owned()))], // }], - //}]; - - // TODO call wg.remove_interface on program exit using a drop impl on an 'Interface' struct - // containing the Network and WGApi + //})].into_iter().collect(); let mut hostfile = match File::open("/etc/hosts") { Ok(mut f) => { @@ -121,13 +150,18 @@ pub fn daemon() -> Result<(), DaemonError> { } }; - for nw in config.networks { - let wg = WGApi::new(nw.name.clone(), false)?; + let mut state = State { + conf: config, + apis: HashMap::new(), + }; + + for (name, nw) in &state.conf.networks { + let wg = WGApi::new(name.clone(), false)?; let defguard_peers = nw .peers .iter() - .map(|p| Peer { - public_key: p.pubkey.clone(), + .map(|(peer_key, p)| Peer { + public_key: peer_key.clone(), preshared_key: p.psk.clone(), protocol_version: None, endpoint: p @@ -151,16 +185,16 @@ pub fn daemon() -> Result<(), DaemonError> { .collect(); wg.create_interface()?; wg.configure_interface(&InterfaceConfiguration { - name: nw.name.clone(), - prvkey: nw.privkey, - address: nw.address, + name: name.clone(), + prvkey: nw.privkey.clone(), + address: nw.address.clone(), port: nw.listen_port as u32, peers: defguard_peers, })?; if let Some((hosts_str, hosts)) = &mut hostfile { nw.peers - .iter() + .values() .map(|peer| { if peer.use_hostnames { peer.ips @@ -181,7 +215,9 @@ pub fn daemon() -> Result<(), DaemonError> { .count(); } - info!("loaded configuration for {0}", nw.name); + state.apis.insert(name.clone(), wg); + + info!("loaded configuration for {0}", name); } info!("loaded all existing configurations"); @@ -190,10 +226,71 @@ pub fn daemon() -> Result<(), DaemonError> { let mut f = AtomicWriteFile::open("/etc/hosts")?; f.write(hosts_str.as_bytes())?; - f.commit(); + f.commit()?; } - // TODO open dbus & network interfaces + let state = Arc::new(RwLock::new(state)); + + let rt = Builder::new_current_thread().enable_all().build()?; + rt.block_on(run_listeners(state))?; + + Ok(()) +} + +async fn run_listeners(state: Arc<RwLock<State>>) -> Result<(), DaemonError> { + for (name, nw) in &state.read().await.conf.networks { + let addr = IpAddrMask::from_str(&nw.address)?.ip; + let listener = TcpListener::bind((addr, nw.mäsch_port)).await?; + + task::spawn(make_fatal(run_network( + state.clone(), + listener, + name.clone(), + ))); + } + + let mut cr = Crossroads::new(); + let if_token = cr.register("de.69owo.maesch", |b| { + b.signal::<(String, String), _>("Proposal", ("network", "peer_data")); + //b.method_with_cr_async("MigrateQuick"); + }); + + cr.insert("/de/69owo/maesch", &[if_token], state.clone()); + + let (res, c) = dbus_tokio::connection::new_session_sync()?; + let _ = tokio::spawn(make_fatal(async { + res.await; + Result::<!, &'static str>::Err("lost connection to dbus!") + })); + + c.start_receive( + MatchRule::new_method_call(), + Box::new(move |msg, conn| { + cr.handle_message(msg, conn).unwrap(); + true + }), + ); + + c.request_name("de.69owo.maesch", true, true, false).await?; + future::pending::<!>().await +} + +async fn make_fatal<E: std::fmt::Display, O, F: std::future::Future<Output = Result<O, E>>>( + f: F, +) -> () { + match f.await { + Err(e) => { + eprintln!("oh no: {e}"); + std::process::exit(1); + } + _ => (), + }; +} +async fn run_network( + state: Arc<RwLock<State>>, + sock: TcpListener, + nw_name: String, +) -> Result<(), DaemonError> { Ok(()) } diff --git a/src/main.rs b/src/main.rs index d85da33..c61306c 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,4 +1,5 @@ #![feature(let_chains)] +#![feature(never_type)] pub mod daemon; |