1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
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(())
}
|