summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/daemon.rs153
-rw-r--r--src/main.rs1
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;