diff options
Diffstat (limited to 'src/daemon_config.rs')
-rw-r--r-- | src/daemon_config.rs | 121 |
1 files changed, 121 insertions, 0 deletions
diff --git a/src/daemon_config.rs b/src/daemon_config.rs new file mode 100644 index 0000000..246a736 --- /dev/null +++ b/src/daemon_config.rs @@ -0,0 +1,121 @@ +use atomic_write_file::AtomicWriteFile; +use defguard_wireguard_rs::{key::Key, net::IpAddrMask}; +use log::info; +use serde::{Deserialize, Serialize}; +use std::{ + collections::{BTreeSet, HashMap}, + fs::File, + io::{ErrorKind, Read, Write}, + net::SocketAddr, + path::PathBuf, + time::SystemTime, +}; +use xdg::BaseDirectories; + +use crate::daemon::DaemonError; + +#[derive(Serialize, Deserialize, Clone)] +pub enum Endpoint { + Ip(SocketAddr), + Domain(String, u16), +} + +// subset of defguard_wireguard_rs::host::Peer, with hostname added +#[derive(Serialize, Deserialize)] +pub struct PeerConfig { + pub psk: Option<Key>, + pub ips: Vec<(IpAddrMask, Option<String>)>, + // if false: the hostnames are kept around for sharing, but we personally do not use them + pub use_hostnames: bool, + pub endpoint: Option<Endpoint>, + + pub last_changed: SystemTime, + pub known_to: Vec<usize>, + + pub mäsch_endpoint: SocketAddr, +} + +fn default_wg_port() -> u16 { + 51820 +} + +#[derive(Serialize, Deserialize)] +pub struct Network { + pub privkey: String, + // this really should be a different type, but this is what defguard takes... + pub address: String, + #[serde(default = "default_wg_port")] + pub listen_port: u16, + pub peers: HashMap<Key, PeerConfig>, + + pub mäsch_port: u16, +} + +#[derive(Serialize, Deserialize, Default)] +pub struct Config { + pub networks: HashMap<String, Network>, +} + +pub fn load_config() -> Result<(PathBuf, Config), DaemonError> { + let config_path = BaseDirectories::with_prefix("mäsch")?.place_state_file("daemon.json")?; + let config: Config = match File::open(&config_path) { + Ok(f) => serde_json::from_reader(f)?, + Err(e) => match e.kind() { + ErrorKind::NotFound => Config::default(), + _ => Err(e)?, + }, + }; + info!("read config"); + + Ok((config_path, config)) +} + +pub fn write_config(conf: &Config, path: &PathBuf) -> Result<(), std::io::Error> { + let mut f = AtomicWriteFile::open(path)?; + serde_json::to_writer(&mut f, conf)?; + f.commit()?; + + Ok(()) +} + +pub fn sync_hostnames(hostnames: &BTreeSet<(String, String)>) -> Result<(), std::io::Error> { + let mut hosts_str = match File::open("/etc/hosts") { + Ok(mut f) => { + let mut r = String::new(); + f.read_to_string(&mut r)?; + r + } + Err(e) => match e.kind() { + ErrorKind::NotFound => "".to_owned(), + _ => return Err(e), + }, + }; + + let seen_hostnames: BTreeSet<String> = hosts_str + .lines() + .map(|l| { + l.split_whitespace() + .take_while(|dom| dom.chars().next().unwrap() != '#') + .skip(1) + }) + .flatten() + .map(|dom| dom.to_owned()) + .collect(); + + for (ip, dom) in hostnames { + if !seen_hostnames.contains(dom.as_str()) { + hosts_str.push_str(ip); + hosts_str.push(' '); + hosts_str.push_str(&format!("{dom}")); + hosts_str.push('\n'); + } + } + + info!("Syncing host file"); + + let mut f = AtomicWriteFile::open("/etc/hosts")?; + f.write(hosts_str.as_bytes())?; + f.commit()?; + + Ok(()) +} |