diff options
author | Simon Garrelou <simon@sixfoisneuf.fr> | 2023-04-16 21:27:34 +0200 |
---|---|---|
committer | Simon Garrelou <simon@sixfoisneuf.fr> | 2023-04-27 15:59:59 +0200 |
commit | a1b23498ddb6f6d0129528bd295dfb490be723da (patch) | |
tree | df55d2dae763e5bccdd27bf776938698bd39b954 | |
parent | 5871891af447e0a49108309c60af64fe9a6f59bc (diff) | |
download | wgmgr-a1b23498ddb6f6d0129528bd295dfb490be723da.tar.gz wgmgr-a1b23498ddb6f6d0129528bd295dfb490be723da.zip |
reoganize project + start cli
-rw-r--r-- | Cargo.lock | 275 | ||||
-rw-r--r-- | Cargo.toml | 2 | ||||
-rw-r--r-- | src/main.rs | 117 | ||||
-rw-r--r-- | src/wg.rs | 3 | ||||
-rw-r--r-- | src/wg/config.rs (renamed from src/lib.rs) | 125 | ||||
-rw-r--r-- | src/wg/error.rs | 31 | ||||
-rw-r--r-- | src/wg/peer.rs | 56 |
7 files changed, 535 insertions, 74 deletions
@@ -3,5 +3,280 @@ | |||
3 | version = 3 | 3 | version = 3 |
4 | 4 | ||
5 | [[package]] | 5 | [[package]] |
6 | name = "anstream" | ||
7 | version = "0.3.0" | ||
8 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
9 | checksum = "9e579a7752471abc2a8268df8b20005e3eadd975f585398f17efcfd8d4927371" | ||
10 | dependencies = [ | ||
11 | "anstyle", | ||
12 | "anstyle-parse", | ||
13 | "anstyle-query", | ||
14 | "anstyle-wincon", | ||
15 | "colorchoice", | ||
16 | "is-terminal", | ||
17 | "utf8parse", | ||
18 | ] | ||
19 | |||
20 | [[package]] | ||
21 | name = "anstyle" | ||
22 | version = "1.0.0" | ||
23 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
24 | checksum = "41ed9a86bf92ae6580e0a31281f65a1b1d867c0cc68d5346e2ae128dddfa6a7d" | ||
25 | |||
26 | [[package]] | ||
27 | name = "anstyle-parse" | ||
28 | version = "0.2.0" | ||
29 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
30 | checksum = "e765fd216e48e067936442276d1d57399e37bce53c264d6fefbe298080cb57ee" | ||
31 | dependencies = [ | ||
32 | "utf8parse", | ||
33 | ] | ||
34 | |||
35 | [[package]] | ||
36 | name = "anstyle-query" | ||
37 | version = "1.0.0" | ||
38 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
39 | checksum = "5ca11d4be1bab0c8bc8734a9aa7bf4ee8316d462a08c6ac5052f888fef5b494b" | ||
40 | dependencies = [ | ||
41 | "windows-sys", | ||
42 | ] | ||
43 | |||
44 | [[package]] | ||
45 | name = "anstyle-wincon" | ||
46 | version = "1.0.0" | ||
47 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
48 | checksum = "4bcd8291a340dd8ac70e18878bc4501dd7b4ff970cfa21c207d36ece51ea88fd" | ||
49 | dependencies = [ | ||
50 | "anstyle", | ||
51 | "windows-sys", | ||
52 | ] | ||
53 | |||
54 | [[package]] | ||
55 | name = "bitflags" | ||
56 | version = "1.3.2" | ||
57 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
58 | checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" | ||
59 | |||
60 | [[package]] | ||
61 | name = "cc" | ||
62 | version = "1.0.79" | ||
63 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
64 | checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f" | ||
65 | |||
66 | [[package]] | ||
67 | name = "clap" | ||
68 | version = "4.2.2" | ||
69 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
70 | checksum = "9b802d85aaf3a1cdb02b224ba472ebdea62014fccfcb269b95a4d76443b5ee5a" | ||
71 | dependencies = [ | ||
72 | "clap_builder", | ||
73 | ] | ||
74 | |||
75 | [[package]] | ||
76 | name = "clap_builder" | ||
77 | version = "4.2.2" | ||
78 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
79 | checksum = "14a1a858f532119338887a4b8e1af9c60de8249cd7bafd68036a489e261e37b6" | ||
80 | dependencies = [ | ||
81 | "anstream", | ||
82 | "anstyle", | ||
83 | "bitflags", | ||
84 | "clap_lex", | ||
85 | "once_cell", | ||
86 | "strsim", | ||
87 | ] | ||
88 | |||
89 | [[package]] | ||
90 | name = "clap_lex" | ||
91 | version = "0.4.1" | ||
92 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
93 | checksum = "8a2dd5a6fe8c6e3502f568a6353e5273bbb15193ad9a89e457b9970798efbea1" | ||
94 | |||
95 | [[package]] | ||
96 | name = "colorchoice" | ||
97 | version = "1.0.0" | ||
98 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
99 | checksum = "acbf1af155f9b9ef647e42cdc158db4b64a1b61f743629225fde6f3e0be2a7c7" | ||
100 | |||
101 | [[package]] | ||
102 | name = "errno" | ||
103 | version = "0.3.1" | ||
104 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
105 | checksum = "4bcfec3a70f97c962c307b2d2c56e358cf1d00b558d74262b5f929ee8cc7e73a" | ||
106 | dependencies = [ | ||
107 | "errno-dragonfly", | ||
108 | "libc", | ||
109 | "windows-sys", | ||
110 | ] | ||
111 | |||
112 | [[package]] | ||
113 | name = "errno-dragonfly" | ||
114 | version = "0.1.2" | ||
115 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
116 | checksum = "aa68f1b12764fab894d2755d2518754e71b4fd80ecfb822714a1206c2aab39bf" | ||
117 | dependencies = [ | ||
118 | "cc", | ||
119 | "libc", | ||
120 | ] | ||
121 | |||
122 | [[package]] | ||
123 | name = "hermit-abi" | ||
124 | version = "0.3.1" | ||
125 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
126 | checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" | ||
127 | |||
128 | [[package]] | ||
129 | name = "io-lifetimes" | ||
130 | version = "1.0.10" | ||
131 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
132 | checksum = "9c66c74d2ae7e79a5a8f7ac924adbe38ee42a859c6539ad869eb51f0b52dc220" | ||
133 | dependencies = [ | ||
134 | "hermit-abi", | ||
135 | "libc", | ||
136 | "windows-sys", | ||
137 | ] | ||
138 | |||
139 | [[package]] | ||
140 | name = "ipnetwork" | ||
141 | version = "0.20.0" | ||
142 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
143 | checksum = "bf466541e9d546596ee94f9f69590f89473455f88372423e0008fc1a7daf100e" | ||
144 | dependencies = [ | ||
145 | "serde", | ||
146 | ] | ||
147 | |||
148 | [[package]] | ||
149 | name = "is-terminal" | ||
150 | version = "0.4.7" | ||
151 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
152 | checksum = "adcf93614601c8129ddf72e2d5633df827ba6551541c6d8c59520a371475be1f" | ||
153 | dependencies = [ | ||
154 | "hermit-abi", | ||
155 | "io-lifetimes", | ||
156 | "rustix", | ||
157 | "windows-sys", | ||
158 | ] | ||
159 | |||
160 | [[package]] | ||
161 | name = "libc" | ||
162 | version = "0.2.141" | ||
163 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
164 | checksum = "3304a64d199bb964be99741b7a14d26972741915b3649639149b2479bb46f4b5" | ||
165 | |||
166 | [[package]] | ||
167 | name = "linux-raw-sys" | ||
168 | version = "0.3.1" | ||
169 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
170 | checksum = "d59d8c75012853d2e872fb56bc8a2e53718e2cafe1a4c823143141c6d90c322f" | ||
171 | |||
172 | [[package]] | ||
173 | name = "once_cell" | ||
174 | version = "1.17.1" | ||
175 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
176 | checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3" | ||
177 | |||
178 | [[package]] | ||
179 | name = "rustix" | ||
180 | version = "0.37.11" | ||
181 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
182 | checksum = "85597d61f83914ddeba6a47b3b8ffe7365107221c2e557ed94426489fefb5f77" | ||
183 | dependencies = [ | ||
184 | "bitflags", | ||
185 | "errno", | ||
186 | "io-lifetimes", | ||
187 | "libc", | ||
188 | "linux-raw-sys", | ||
189 | "windows-sys", | ||
190 | ] | ||
191 | |||
192 | [[package]] | ||
193 | name = "serde" | ||
194 | version = "1.0.160" | ||
195 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
196 | checksum = "bb2f3770c8bce3bcda7e149193a069a0f4365bda1fa5cd88e03bca26afc1216c" | ||
197 | |||
198 | [[package]] | ||
199 | name = "strsim" | ||
200 | version = "0.10.0" | ||
201 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
202 | checksum = "73473c0e59e6d5812c5dfe2a064a6444949f089e20eec9a2e5506596494e4623" | ||
203 | |||
204 | [[package]] | ||
205 | name = "utf8parse" | ||
206 | version = "0.2.1" | ||
207 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
208 | checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" | ||
209 | |||
210 | [[package]] | ||
6 | name = "wgmgr" | 211 | name = "wgmgr" |
7 | version = "0.1.0" | 212 | version = "0.1.0" |
213 | dependencies = [ | ||
214 | "clap", | ||
215 | "ipnetwork", | ||
216 | ] | ||
217 | |||
218 | [[package]] | ||
219 | name = "windows-sys" | ||
220 | version = "0.48.0" | ||
221 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
222 | checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" | ||
223 | dependencies = [ | ||
224 | "windows-targets", | ||
225 | ] | ||
226 | |||
227 | [[package]] | ||
228 | name = "windows-targets" | ||
229 | version = "0.48.0" | ||
230 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
231 | checksum = "7b1eb6f0cd7c80c79759c929114ef071b87354ce476d9d94271031c0497adfd5" | ||
232 | dependencies = [ | ||
233 | "windows_aarch64_gnullvm", | ||
234 | "windows_aarch64_msvc", | ||
235 | "windows_i686_gnu", | ||
236 | "windows_i686_msvc", | ||
237 | "windows_x86_64_gnu", | ||
238 | "windows_x86_64_gnullvm", | ||
239 | "windows_x86_64_msvc", | ||
240 | ] | ||
241 | |||
242 | [[package]] | ||
243 | name = "windows_aarch64_gnullvm" | ||
244 | version = "0.48.0" | ||
245 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
246 | checksum = "91ae572e1b79dba883e0d315474df7305d12f569b400fcf90581b06062f7e1bc" | ||
247 | |||
248 | [[package]] | ||
249 | name = "windows_aarch64_msvc" | ||
250 | version = "0.48.0" | ||
251 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
252 | checksum = "b2ef27e0d7bdfcfc7b868b317c1d32c641a6fe4629c171b8928c7b08d98d7cf3" | ||
253 | |||
254 | [[package]] | ||
255 | name = "windows_i686_gnu" | ||
256 | version = "0.48.0" | ||
257 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
258 | checksum = "622a1962a7db830d6fd0a69683c80a18fda201879f0f447f065a3b7467daa241" | ||
259 | |||
260 | [[package]] | ||
261 | name = "windows_i686_msvc" | ||
262 | version = "0.48.0" | ||
263 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
264 | checksum = "4542c6e364ce21bf45d69fdd2a8e455fa38d316158cfd43b3ac1c5b1b19f8e00" | ||
265 | |||
266 | [[package]] | ||
267 | name = "windows_x86_64_gnu" | ||
268 | version = "0.48.0" | ||
269 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
270 | checksum = "ca2b8a661f7628cbd23440e50b05d705db3686f894fc9580820623656af974b1" | ||
271 | |||
272 | [[package]] | ||
273 | name = "windows_x86_64_gnullvm" | ||
274 | version = "0.48.0" | ||
275 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
276 | checksum = "7896dbc1f41e08872e9d5e8f8baa8fdd2677f29468c4e156210174edc7f7b953" | ||
277 | |||
278 | [[package]] | ||
279 | name = "windows_x86_64_msvc" | ||
280 | version = "0.48.0" | ||
281 | source = "registry+https://github.com/rust-lang/crates.io-index" | ||
282 | checksum = "1a515f5799fe4961cb532f983ce2b23082366b898e52ffbce459c86f67c8378a" | ||
@@ -6,3 +6,5 @@ edition = "2021" | |||
6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | 6 | # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html |
7 | 7 | ||
8 | [dependencies] | 8 | [dependencies] |
9 | clap = { version = "4.2.2", features = ["cargo"] } | ||
10 | ipnetwork = "0.20.0" | ||
diff --git a/src/main.rs b/src/main.rs index 028e0e2..6c4787e 100644 --- a/src/main.rs +++ b/src/main.rs | |||
@@ -1,3 +1,118 @@ | |||
1 | use std::{env, process::exit}; | ||
2 | use std::error::Error; | ||
3 | |||
4 | use clap::command; | ||
5 | use clap::Command; | ||
6 | use clap::arg; | ||
7 | |||
8 | mod wg; | ||
9 | |||
1 | fn main() { | 10 | fn main() { |
2 | println!("{:?}", wgmgr::WireguardConfig::new("wg.conf").unwrap()) | 11 | let matches = command!() |
12 | .subcommand_required(true) | ||
13 | .subcommand( | ||
14 | Command::new("ls") | ||
15 | .about("List known clients") | ||
16 | ) | ||
17 | .subcommand( | ||
18 | Command::new("config") | ||
19 | .about("Generate the configuration file for a client") | ||
20 | .arg( | ||
21 | arg!( | ||
22 | -t --type <TYPE> "Type of configuration: \"split\" or \"full\"" | ||
23 | ) | ||
24 | .required(false) | ||
25 | ) | ||
26 | .arg(arg!(<PEER> "Name of the peer")) | ||
27 | ) | ||
28 | .get_matches(); | ||
29 | |||
30 | let conf_path = match env::var("WG_CONF") { | ||
31 | Ok(s) => s, | ||
32 | Err(_) => String::from("/etc/wireguard/wg0.conf") | ||
33 | }; | ||
34 | |||
35 | let conf = match wg::config::WireguardConfig::new(&conf_path) { | ||
36 | Ok(c) => c, | ||
37 | Err(e) => { | ||
38 | eprintln!("Error loading the configuration file '{}'", conf_path); | ||
39 | eprintln!("{}", e); | ||
40 | exit(1); | ||
41 | } | ||
42 | }; | ||
43 | |||
44 | match matches.subcommand() { | ||
45 | Some(("ls", _)) => { | ||
46 | do_list(&conf); | ||
47 | } | ||
48 | |||
49 | Some(("config", args)) => { | ||
50 | match args.get_one::<String>("PEER") { | ||
51 | Some(peer_name) => { | ||
52 | let is_full = match args.get_one::<String>("type") { | ||
53 | Some(t) => { | ||
54 | match t.to_string().as_str() { | ||
55 | "split" => false, | ||
56 | "full" => true, | ||
57 | _ => { | ||
58 | eprintln!("Error: '--type' must be either 'split' or 'full'"); | ||
59 | exit(3); | ||
60 | } | ||
61 | } | ||
62 | }, | ||
63 | None => false | ||
64 | }; | ||
65 | |||
66 | do_config(&conf, peer_name.to_string(), is_full).unwrap(); | ||
67 | }, | ||
68 | None => {} | ||
69 | } | ||
70 | } | ||
71 | |||
72 | _ => { | ||
73 | unimplemented!(); | ||
74 | } | ||
75 | } | ||
76 | |||
3 | } | 77 | } |
78 | |||
79 | fn do_list(conf: &wg::config::WireguardConfig) { | ||
80 | let mut max_length = 0; | ||
81 | for p in conf.peers.iter() { | ||
82 | if p.name.len() > max_length { | ||
83 | max_length = p.name.len(); | ||
84 | } | ||
85 | } | ||
86 | |||
87 | for p in conf.peers.iter() { | ||
88 | println!("{:max_length$} | {}", p.name, p.ip); | ||
89 | } | ||
90 | } | ||
91 | |||
92 | fn do_config(conf: &wg::config::WireguardConfig, peer_name: String, is_full: bool) -> Result<(), Box<dyn Error>> { | ||
93 | let peer = match conf.peers.iter().filter(|p| { p.name == peer_name }).nth(0) { | ||
94 | Some(p) => p, | ||
95 | None => { | ||
96 | eprintln!("No such peer: {}", peer_name); | ||
97 | exit(1); | ||
98 | } | ||
99 | }; | ||
100 | |||
101 | println!("[Interface]"); | ||
102 | println!("PrivateKey = {}", peer.private_key()?); | ||
103 | println!("Address = {}/32", peer.ip); | ||
104 | println!("DNS = TODO\n"); | ||
105 | |||
106 | println!("[Peer]"); | ||
107 | println!("PublicKey = TODO"); | ||
108 | |||
109 | let allowed_ips = match is_full { | ||
110 | true => String::from("0.0.0.0/0"), | ||
111 | false => conf.network.to_string() | ||
112 | }; | ||
113 | println!("AllowedIPs = {}", allowed_ips); | ||
114 | println!("Endpoint = TODO"); | ||
115 | println!("PersistentKeepAlive = 25"); | ||
116 | |||
117 | Ok(()) | ||
118 | } \ No newline at end of file | ||
diff --git a/src/wg.rs b/src/wg.rs new file mode 100644 index 0000000..39cfc0c --- /dev/null +++ b/src/wg.rs | |||
@@ -0,0 +1,3 @@ | |||
1 | mod error; | ||
2 | pub mod config; | ||
3 | mod peer; \ No newline at end of file | ||
diff --git a/src/lib.rs b/src/wg/config.rs index c9bce25..03d2ce0 100644 --- a/src/lib.rs +++ b/src/wg/config.rs | |||
@@ -1,52 +1,22 @@ | |||
1 | use std::env::join_paths; | 1 | use std::str::FromStr; |
2 | use std::fs::read_to_string; | ||
3 | use std::{net::Ipv4Addr, fs, error::Error}; | 2 | use std::{net::Ipv4Addr, fs, error::Error}; |
4 | use std::fmt::{Formatter, Display}; | ||
5 | 3 | ||
6 | #[derive(Debug)] | 4 | use ipnetwork::Ipv4Network; |
7 | struct WgMgrError { | ||
8 | msg: String | ||
9 | } | ||
10 | |||
11 | impl WgMgrError { | ||
12 | fn new(msg: String) -> WgMgrError { | ||
13 | WgMgrError { | ||
14 | msg | ||
15 | } | ||
16 | } | ||
17 | } | ||
18 | |||
19 | impl Error for WgMgrError { | ||
20 | fn description(&self) -> &str { | ||
21 | return self.msg.as_str() | ||
22 | } | ||
23 | 5 | ||
24 | fn cause(&self) -> Option<&dyn Error> { | 6 | use crate::wg::error::WgMgrError; |
25 | None | 7 | use crate::wg::peer::Peer; |
26 | } | ||
27 | } | ||
28 | |||
29 | impl Display for WgMgrError { | ||
30 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||
31 | write!(f, "{}", self.msg) | ||
32 | } | ||
33 | } | ||
34 | 8 | ||
35 | #[derive(Debug)] | 9 | #[derive(Debug)] |
36 | pub struct WireguardConfig { | 10 | pub struct WireguardConfig { |
37 | private_key: String, | 11 | private_key: String, |
38 | listen_port: u32, | 12 | listen_port: u32, |
39 | address: Ipv4Addr, | 13 | pub network: Ipv4Network, |
40 | net_range: u32, | 14 | pub peers: Vec<Peer>, |
41 | peers: Vec<Peer> | 15 | pre_ups: Vec<String>, |
16 | post_downs: Vec<String> | ||
42 | } | 17 | } |
43 | 18 | ||
44 | #[derive(Debug)] | 19 | |
45 | struct Peer { | ||
46 | name: String, | ||
47 | public_key: String, | ||
48 | ip: Ipv4Addr | ||
49 | } | ||
50 | 20 | ||
51 | impl WireguardConfig { | 21 | impl WireguardConfig { |
52 | pub fn new(config_path: &str) -> Result<WireguardConfig, Box<dyn Error>> { | 22 | pub fn new(config_path: &str) -> Result<WireguardConfig, Box<dyn Error>> { |
@@ -55,9 +25,10 @@ impl WireguardConfig { | |||
55 | let mut conf = WireguardConfig{ | 25 | let mut conf = WireguardConfig{ |
56 | private_key: String::new(), | 26 | private_key: String::new(), |
57 | listen_port: 0, | 27 | listen_port: 0, |
58 | address: Ipv4Addr::UNSPECIFIED, | 28 | network: Ipv4Network::from_str("0.0.0.0/0")?, |
59 | net_range: 0, | 29 | peers: vec![], |
60 | peers: vec![] | 30 | pre_ups: vec![], |
31 | post_downs: vec![] | ||
61 | }; | 32 | }; |
62 | let mut current_peer = Peer { | 33 | let mut current_peer = Peer { |
63 | ip: Ipv4Addr::UNSPECIFIED, | 34 | ip: Ipv4Addr::UNSPECIFIED, |
@@ -107,36 +78,29 @@ impl WireguardConfig { | |||
107 | } | 78 | } |
108 | else if line.starts_with("Address") { | 79 | else if line.starts_with("Address") { |
109 | let addr = config_value(line)?; | 80 | let addr = config_value(line)?; |
110 | match addr.find("/") { | 81 | conf.network = Ipv4Network::from_str(addr)?; |
111 | Some(idx) => { | 82 | } |
112 | conf.address = addr[..idx].parse()?; | 83 | else if line.starts_with("PreUp") { |
113 | conf.net_range = addr[idx+1..].parse()?; | 84 | let pre_up = String::from(config_value(line)?); |
114 | } | 85 | conf.pre_ups.push(pre_up); |
115 | None => { | 86 | } |
116 | return Err(Box::new(WgMgrError::new(String::from("'Interface' > 'Address' is missing a subnet")))); | 87 | else if line.starts_with("PostDown") { |
117 | } | 88 | let post_down = String::from(config_value(line)?); |
118 | } | 89 | conf.post_downs.push(post_down); |
119 | } | 90 | } |
120 | } | 91 | } |
121 | 92 | ||
122 | else if in_peer { | 93 | else if in_peer { |
123 | if line.starts_with("AllowedIPs") { | 94 | if line.starts_with("AllowedIPs") { |
124 | let addr = config_value(line)?; | 95 | let addr = config_value(line)?; |
125 | match addr.find("/") { | 96 | let ip = Ipv4Network::from_str(addr)?; |
126 | Some(idx) => { | 97 | |
127 | current_peer.ip = addr[..idx].parse()?; | 98 | if ip.prefix() != 32 { |
128 | let net_range: i32 = addr[idx+1..].parse()?; | 99 | let msg = format!("Peer '{}' has invalid net range {}", current_peer.name, ip.prefix()); |
129 | 100 | return Err(Box::new(WgMgrError::new(msg))); | |
130 | if net_range != 32 { | ||
131 | let msg = format!("Peer '{}' has invalid net range {}", current_peer.name, net_range); | ||
132 | return Err(Box::new(WgMgrError::new(msg))); | ||
133 | } | ||
134 | } | ||
135 | None => { | ||
136 | let msg = format!("Peer '{}' is missing net range", current_peer.name); | ||
137 | return Err(Box::new(WgMgrError::new(msg))); | ||
138 | } | ||
139 | } | 101 | } |
102 | |||
103 | current_peer.ip = ip.ip(); | ||
140 | } | 104 | } |
141 | else if line.starts_with("PublicKey") { | 105 | else if line.starts_with("PublicKey") { |
142 | current_peer.public_key = String::from(config_value(line)?); | 106 | current_peer.public_key = String::from(config_value(line)?); |
@@ -152,21 +116,36 @@ impl WireguardConfig { | |||
152 | 116 | ||
153 | Ok(conf) | 117 | Ok(conf) |
154 | } | 118 | } |
155 | } | ||
156 | 119 | ||
157 | impl Peer { | ||
158 | pub fn private_key(&self) -> Result<String, Box<dyn Error>> { | ||
159 | // TODO: do not hardcode where private keys are stored | ||
160 | 120 | ||
161 | let pk_folder = String::from("./private_keys/"); | 121 | pub fn next_free_ip(&self) -> Result<Ipv4Addr, WgMgrError> { |
162 | let pk_path = join_paths(&[pk_folder, self.name.clone()])?; | 122 | let mut iter = self.network.iter(); |
123 | iter.next(); // Skip the first IP (identification) | ||
163 | 124 | ||
164 | let pk = read_to_string(pk_path)?; | 125 | for ip in iter{ |
126 | if ip == self.network.ip(){ | ||
127 | continue; | ||
128 | } | ||
165 | 129 | ||
166 | Ok(pk) | 130 | let mut ok = true; |
131 | for peer in self.peers.iter() { | ||
132 | if peer.ip == ip { | ||
133 | ok = false; | ||
134 | break; | ||
135 | } | ||
136 | } | ||
137 | |||
138 | if ok { | ||
139 | return Ok(ip); | ||
140 | } | ||
141 | } | ||
142 | |||
143 | Err(WgMgrError::new(String::from("No more free IP addresses"))) | ||
167 | } | 144 | } |
168 | } | 145 | } |
169 | 146 | ||
147 | |||
148 | |||
170 | fn config_value(line: &str) -> Result<&str, &str> { | 149 | fn config_value(line: &str) -> Result<&str, &str> { |
171 | match line.find("=") { | 150 | match line.find("=") { |
172 | Some(i) => { | 151 | Some(i) => { |
diff --git a/src/wg/error.rs b/src/wg/error.rs new file mode 100644 index 0000000..0167b22 --- /dev/null +++ b/src/wg/error.rs | |||
@@ -0,0 +1,31 @@ | |||
1 | use std::fmt::{Formatter, Display}; | ||
2 | use std::error::Error; | ||
3 | |||
4 | #[derive(Debug)] | ||
5 | pub struct WgMgrError { | ||
6 | msg: String | ||
7 | } | ||
8 | |||
9 | impl WgMgrError { | ||
10 | pub fn new(msg: String) -> WgMgrError { | ||
11 | WgMgrError { | ||
12 | msg | ||
13 | } | ||
14 | } | ||
15 | } | ||
16 | |||
17 | impl Error for WgMgrError { | ||
18 | fn description(&self) -> &str { | ||
19 | return self.msg.as_str() | ||
20 | } | ||
21 | |||
22 | fn cause(&self) -> Option<&dyn Error> { | ||
23 | None | ||
24 | } | ||
25 | } | ||
26 | |||
27 | impl Display for WgMgrError { | ||
28 | fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { | ||
29 | write!(f, "{}", self.msg) | ||
30 | } | ||
31 | } \ No newline at end of file | ||
diff --git a/src/wg/peer.rs b/src/wg/peer.rs new file mode 100644 index 0000000..0a25841 --- /dev/null +++ b/src/wg/peer.rs | |||
@@ -0,0 +1,56 @@ | |||
1 | use std::error::Error; | ||
2 | use std::net::Ipv4Addr; | ||
3 | use std::env::join_paths; | ||
4 | use std::fs::read_to_string; | ||
5 | |||
6 | #[derive(Debug)] | ||
7 | pub struct Peer { | ||
8 | pub name: String, | ||
9 | pub public_key: String, | ||
10 | pub ip: Ipv4Addr | ||
11 | } | ||
12 | |||
13 | impl Peer { | ||
14 | pub fn private_key(&self) -> Result<String, Box<dyn Error>> { | ||
15 | // TODO: do not hardcode where private keys are stored | ||
16 | |||
17 | let pk_folder = String::from("./private_keys/"); | ||
18 | let pk_path = join_paths(&[pk_folder, self.name.clone()])?; | ||
19 | |||
20 | let pk = read_to_string(pk_path)?; | ||
21 | |||
22 | Ok(pk) | ||
23 | } | ||
24 | } | ||
25 | |||
26 | impl PartialOrd for Peer { | ||
27 | fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> { | ||
28 | return self.ip.partial_cmp(&other.ip) | ||
29 | } | ||
30 | |||
31 | fn ge(&self, other: &Self) -> bool { | ||
32 | return self.ip.ge(&other.ip) | ||
33 | } | ||
34 | |||
35 | fn gt(&self, other: &Self) -> bool { | ||
36 | return self.ip.gt(&other.ip) | ||
37 | } | ||
38 | |||
39 | fn le(&self, other: &Self) -> bool { | ||
40 | return self.ip.le(&other.ip) | ||
41 | } | ||
42 | |||
43 | fn lt(&self, other: &Self) -> bool { | ||
44 | return self.ip.lt(&other.ip) | ||
45 | } | ||
46 | } | ||
47 | |||
48 | impl PartialEq for Peer { | ||
49 | fn eq(&self, other: &Self) -> bool { | ||
50 | return self.ip.eq(&other.ip) | ||
51 | } | ||
52 | |||
53 | fn ne(&self, other: &Self) -> bool { | ||
54 | return self.ip.ne(&other.ip) | ||
55 | } | ||
56 | } \ No newline at end of file | ||