summaryrefslogtreecommitdiff
path: root/src/daemon.rs
diff options
context:
space:
mode:
authorLia Lenckowski <lialenck@protonmail.com>2024-08-12 23:53:30 +0200
committerLia Lenckowski <lialenck@protonmail.com>2024-08-12 23:53:30 +0200
commit80c3c48d5cec4b227389fbc8fcbd10f2abf6bce1 (patch)
tree88e48deb394a04bcb789411bfdc9b21250aba8d8 /src/daemon.rs
parent536153bcefebc59ad5c56d6b27263439bda5e248 (diff)
downloadmaesch-80c3c48d5cec4b227389fbc8fcbd10f2abf6bce1.tar
maesch-80c3c48d5cec4b227389fbc8fcbd10f2abf6bce1.tar.bz2
maesch-80c3c48d5cec4b227389fbc8fcbd10f2abf6bce1.tar.zst
somewhat better-abstracted network adding
Diffstat (limited to 'src/daemon.rs')
-rw-r--r--src/daemon.rs279
1 files changed, 153 insertions, 126 deletions
diff --git a/src/daemon.rs b/src/daemon.rs
index e1404a0..34e4474 100644
--- a/src/daemon.rs
+++ b/src/daemon.rs
@@ -2,19 +2,21 @@ use atomic_write_file::AtomicWriteFile;
use base64::prelude::*;
use core::net::SocketAddr;
use dbus::{channel::MatchingReceiver, message::MatchRule};
-use dbus_crossroads::{Crossroads, MethodErr};
+use dbus_crossroads::{Crossroads, MethodErr, Context};
use defguard_wireguard_rs::{
host::Peer, key::Key, net::IpAddrMask, InterfaceConfiguration, WGApi, WireguardInterfaceApi,
};
use futures::future;
-use log::{info, warn};
+use log::{debug, info, warn};
use rand::Rng;
use serde::{Deserialize, Serialize};
use std::{
collections::{BTreeSet, HashMap},
fs::File,
io::{ErrorKind, Read, Write},
+ ops::DerefMut,
net::ToSocketAddrs,
+ marker::PhantomData,
sync::Arc,
time::SystemTime,
};
@@ -91,16 +93,17 @@ struct Config {
networks: HashMap<String, Network>,
}
+// TODO das überschreibt änderungen an /etc/hosts während der runtime :(
struct State {
conf: Config,
- apis: HashMap<String, WGApi>,
+ nw_handles: HashMap<String, (WGApi, task::JoinHandle<()>)>,
hostfile: Option<(String, BTreeSet<String>)>,
}
impl Drop for State {
fn drop(&mut self) {
- for api in self.apis.values() {
- let _ = api.remove_interface();
+ for (wg_api, _) in self.nw_handles.values() {
+ let _ = wg_api.remove_interface();
}
}
}
@@ -140,42 +143,165 @@ pub fn daemon() -> Result<(), DaemonError> {
}
};
- let mut state = State {
- conf: Config::default(),
- apis: HashMap::new(),
+ let state = Arc::new(RwLock::new(State {
+ conf: config,
+ nw_handles: HashMap::new(),
hostfile,
- };
+ }));
+
+ let rt = Builder::new_current_thread().enable_all().build()?;
+ rt.block_on(init_networks(state))?;
+
+ Ok(())
+}
- for (name, nw) in &config.networks {
- add_network(
- &mut state,
+async fn init_networks(state: Arc<RwLock<State>>) -> Result<(), DaemonError> {
+ let mut state_rw_guard = state.write().await;
+ let state_rw = state_rw_guard.deref_mut();
+
+ for (name, nw) in &state_rw.conf.networks {
+ let wg_api = add_network(
+ &mut state_rw.hostfile,
name.clone(),
nw.privkey.clone(),
nw.address.clone(),
nw.listen_port,
&nw.peers,
- )?;
- info!("loaded configuration for {0}", name);
+ ).await?;
+
+ let addr = IpAddrMask::from_str(&nw.address)?.ip;
+ let h = task::spawn(make_fatal(run_network(
+ state.clone(),
+ TcpListener::bind((addr, nw.mäsch_port)).await?,
+ name.clone(),
+ )));
+
+ state_rw.nw_handles.insert(name.clone(), (wg_api, h));
+
+ debug!("loaded configuration for {0}", name);
}
info!("loaded all existing configurations");
- state.conf = config;
+ drop(state_rw_guard);
- let state = Arc::new(RwLock::new(state));
+ let mut cr = Crossroads::new();
+ let state_ref = state.clone();
- let rt = Builder::new_current_thread().enable_all().build()?;
- rt.block_on(run_listeners(state))?;
+ let if_token = cr.register("de.a.maesch", move |b| {
+ b.signal::<(String, String), _>("Proposal", ("network", "peer_data"));
+ b.method_with_cr_async(
+ "AddNetwork",
+ ("name", "key", "ip", "listen_port", "maesch_port"),
+ ("success",),
+ move |ctx, _, args: (String, String, String, u16, u16)| {
+ debug!("Received AddNetwork");
+ handle_add_network(ctx, state_ref.clone(), args)
+ },
+ );
+ });
- Ok(())
+ cr.insert("/de/a/maesch", &[if_token], ());
+
+ let (res, c) = dbus_tokio::connection::new_system_sync()?;
+ cr.set_async_support(Some((c.clone(), Box::new(|x| { tokio::spawn(x); }))));
+ let _ = tokio::spawn(make_fatal(async {
+ res.await;
+ Result::<!, &'static str>::Err("lost connection to dbus!")
+ }));
+
+ let receive_token = c.start_receive(
+ MatchRule::new_method_call(),
+ Box::new(move |msg, conn| {
+ cr.handle_message(msg, conn).unwrap();
+ true
+ }),
+ );
+
+ c.request_name("de.a.maesch", false, true, false).await?;
+
+ future::pending::<!>().await
+
+ //c.stop_receive(receive_token);
}
-fn add_network(
- state: &mut State,
+async fn handle_add_network(
+ mut ctx: Context,
+ state: Arc<RwLock<State>>,
+ (name, may_key, may_ip, may_lp, may_mp): (String, String, String, u16, u16),
+) -> PhantomData<(bool,)> {
+ // NOTE: this is kinda stupid: we convert to a string later anyways, as thats what
+ // defguard_wg takes...
+ let key = Key::new(match may_key.as_str() {
+ "" => rand::thread_rng().gen(),
+ _ => match BASE64_STANDARD.decode(may_key) {
+ Ok(v) if v.len() == 32 => v.try_into().unwrap(),
+ _ => {
+ warn!("AddNetwork with bad key");
+ return ctx.reply(Err(MethodErr::invalid_arg("bad key")))
+ },
+ },
+ });
+
+ // we store the ip as the original string, but should validate it regardless
+ let (ip, ip_string) = match may_ip.as_str() {
+ "" => todo!(),
+ _ => match IpAddrMask::from_str(&may_ip) {
+ Ok(ip_mask) => (ip_mask.ip, may_ip),
+ Err(_) => {
+ warn!("AddNetwork with bad ip");
+ return ctx.reply(Err(MethodErr::invalid_arg("invalid ip")))
+ },
+ },
+ };
+
+ let lp = if may_lp == 0 { 25565 } else { may_lp };
+ let mp = if may_mp == 0 { 51820 } else { may_mp };
+
+ let mut state_rw_guard = state.write().await;
+ let state_rw = state_rw_guard.deref_mut();
+
+ let wg_api = match add_network(
+ &mut state_rw.hostfile,
+ name.clone(),
+ key.to_string(),
+ ip_string,
+ lp,
+ &HashMap::new(),
+ ).await {
+ Ok(wg_api) => wg_api,
+ Err(e) => {
+ warn!("AddNetwork couldn't add network: {e}");
+ return ctx.reply(Err(MethodErr::failed(&e)))
+ },
+ };
+
+ // TODO ins wg_api
+ let listener = match TcpListener::bind((ip, mp)).await {
+ Ok(l) => l,
+ Err(e) => {
+ let _ = wg_api.remove_interface();
+ warn!("AddNetwork couldn't start listener: {e}");
+ return ctx.reply(Err(MethodErr::failed(&e)))
+ },
+ };
+ let h = task::spawn(make_fatal(run_network(
+ state.clone(),
+ listener,
+ name.clone(),
+ )));
+
+ state_rw.nw_handles.insert(name.clone(), (wg_api, h));
+
+ ctx.reply(Ok((true,)))
+}
+
+async fn add_network(
+ hostfile: &mut Option<(String, BTreeSet<String>)>,
name: String,
privkey: String,
address: String,
port: u16,
peers: &HashMap<Key, PeerConfig>,
-) -> Result<(), DaemonError> {
+) -> Result<WGApi, DaemonError> {
let wg = WGApi::new(name.clone(), false)?;
let defguard_peers = peers
.iter()
@@ -211,7 +337,7 @@ fn add_network(
peers: defguard_peers,
})?;
- if let Some((hosts_str, hosts)) = &mut state.hostfile {
+ if let Some((hosts_str, hosts)) = hostfile {
peers
.values()
.map(|peer| {
@@ -234,115 +360,15 @@ fn add_network(
.count();
}
- state.apis.insert(name, wg);
-
- if let Some((hosts_str, _)) = &state.hostfile {
- info!("proposed next hosts file: {hosts_str}");
+ if let Some((hosts_str, _)) = hostfile {
+ debug!("writing hosts file: {hosts_str}");
let mut f = AtomicWriteFile::open("/etc/hosts")?;
f.write(hosts_str.as_bytes())?;
f.commit()?;
}
- 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 state_ref = state.clone();
-
- let if_token = cr.register("de.69owo.maesch", move |b| {
- b.signal::<(String, String), _>("Proposal", ("network", "peer_data"));
- b.method_with_cr_async(
- "AddNetwork",
- ("name", "key", "ip", "listen_port", "maesch_port"),
- ("success",),
- move |mut ctx,
- cr,
- (name, may_key, may_ip, may_lp, may_mp): (
- String,
- String,
- String,
- u16,
- u16,
- )| {
- let state_ref = state_ref.clone();
- async move {
- // NOTE: this is kinda stupid: we convert to a string later anyways, as thats what
- // defguard_wg takes...
- let key = Key::new(match may_key.as_str() {
- "" => rand::thread_rng().gen(),
- _ => match BASE64_STANDARD.decode(may_key) {
- Ok(v) if v.len() == 32 => v.try_into().unwrap(),
- _ => return ctx.reply(Err(MethodErr::invalid_arg("bad key"))),
- },
- });
-
- // we store the ip as the original string, but should validate it regardless
- let (ip, ip_string) = match may_ip.as_str() {
- "" => todo!(),
- _ => match IpAddrMask::from_str(&may_ip) {
- Err(_) => {
- return ctx.reply(Err(MethodErr::invalid_arg("invalid ip")))
- }
- Ok(ip_mask) => (ip_mask.ip, may_ip),
- },
- };
-
- let lp = if may_lp == 0 { 25565 } else { may_lp };
- let mp = if may_mp == 0 { 51820 } else { may_mp };
-
- let mut st_wr = state_ref.write().await;
-
- match add_network(
- &mut st_wr,
- name,
- key.to_string(),
- ip_string,
- lp,
- &HashMap::new(),
- ) {
- Ok(_) => (),
- Err(e) => return ctx.reply(Err(MethodErr::failed(&e))),
- };
-
- //let listener = TcpListener::bind((ip, mp)).await?;
-
- ctx.reply(Ok((true,)))
- }
- },
- );
- });
-
- 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
+ Ok(wg)
}
async fn make_fatal<E: std::fmt::Display, O, F: std::future::Future<Output = Result<O, E>>>(
@@ -351,6 +377,7 @@ async fn make_fatal<E: std::fmt::Display, O, F: std::future::Future<Output = Res
match f.await {
Err(e) => {
eprintln!("oh no: {e}");
+ // TODO das soll lieber die tokio runtime beenden...
std::process::exit(1);
}
_ => (),