aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Garrelou <simon@sixfoisneuf.fr>2023-04-16 11:46:53 +0200
committerSimon Garrelou <simon@sixfoisneuf.fr>2023-04-16 11:46:53 +0200
commit5871891af447e0a49108309c60af64fe9a6f59bc (patch)
treec0d11c6ee3c4173deeb290391cdfff8c4bdd0fc2
downloadwgmgr-5871891af447e0a49108309c60af64fe9a6f59bc.tar.gz
wgmgr-5871891af447e0a49108309c60af64fe9a6f59bc.zip
First commit: config file parsing
-rw-r--r--.gitignore1
-rw-r--r--Cargo.lock7
-rw-r--r--Cargo.toml8
-rw-r--r--src/lib.rs179
-rw-r--r--src/main.rs3
5 files changed, 198 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..ea8c4bf
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
/target
diff --git a/Cargo.lock b/Cargo.lock
new file mode 100644
index 0000000..7ee9b3d
--- /dev/null
+++ b/Cargo.lock
@@ -0,0 +1,7 @@
1# This file is automatically @generated by Cargo.
2# It is not intended for manual editing.
3version = 3
4
5[[package]]
6name = "wgmgr"
7version = "0.1.0"
diff --git a/Cargo.toml b/Cargo.toml
new file mode 100644
index 0000000..433e4a6
--- /dev/null
+++ b/Cargo.toml
@@ -0,0 +1,8 @@
1[package]
2name = "wgmgr"
3version = "0.1.0"
4edition = "2021"
5
6# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7
8[dependencies]
diff --git a/src/lib.rs b/src/lib.rs
new file mode 100644
index 0000000..c9bce25
--- /dev/null
+++ b/src/lib.rs
@@ -0,0 +1,179 @@
1use std::env::join_paths;
2use std::fs::read_to_string;
3use std::{net::Ipv4Addr, fs, error::Error};
4use std::fmt::{Formatter, Display};
5
6#[derive(Debug)]
7struct WgMgrError {
8 msg: String
9}
10
11impl WgMgrError {
12 fn new(msg: String) -> WgMgrError {
13 WgMgrError {
14 msg
15 }
16 }
17}
18
19impl Error for WgMgrError {
20 fn description(&self) -> &str {
21 return self.msg.as_str()
22 }
23
24 fn cause(&self) -> Option<&dyn Error> {
25 None
26 }
27}
28
29impl Display for WgMgrError {
30 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
31 write!(f, "{}", self.msg)
32 }
33}
34
35#[derive(Debug)]
36pub struct WireguardConfig {
37 private_key: String,
38 listen_port: u32,
39 address: Ipv4Addr,
40 net_range: u32,
41 peers: Vec<Peer>
42}
43
44#[derive(Debug)]
45struct Peer {
46 name: String,
47 public_key: String,
48 ip: Ipv4Addr
49}
50
51impl WireguardConfig {
52 pub fn new(config_path: &str) -> Result<WireguardConfig, Box<dyn Error>> {
53 let f = fs::read_to_string(config_path)?;
54
55 let mut conf = WireguardConfig{
56 private_key: String::new(),
57 listen_port: 0,
58 address: Ipv4Addr::UNSPECIFIED,
59 net_range: 0,
60 peers: vec![]
61 };
62 let mut current_peer = Peer {
63 ip: Ipv4Addr::UNSPECIFIED,
64 name: String::from("N/A"),
65 public_key: String::new()
66 };
67
68 let mut in_interface = false;
69 let mut in_peer = false;
70
71 let mut prev_line: &str = "N/A";
72
73 for line in f.lines() {
74 match line.trim() {
75 "[Interface]" => {
76 in_interface = true;
77 in_peer = false;
78 continue;
79 }
80 "[Peer]" => {
81 in_interface = false;
82 in_peer = true;
83
84 if current_peer.ip != Ipv4Addr::UNSPECIFIED {
85 conf.peers.push(current_peer);
86 }
87
88 current_peer = Peer {
89 ip: Ipv4Addr::UNSPECIFIED,
90 name: String::from(prev_line.trim_start_matches(&['#', ' '])),
91 public_key: String::new()
92 };
93 continue;
94 }
95 "" => {
96 continue;
97 }
98 _ => {}
99 }
100
101 if in_interface {
102 if line.starts_with("PrivateKey") {
103 conf.private_key = String::from(config_value(line)?);
104 }
105 else if line.starts_with("ListenPort") {
106 conf.listen_port = config_value(line)?.parse()?;
107 }
108 else if line.starts_with("Address") {
109 let addr = config_value(line)?;
110 match addr.find("/") {
111 Some(idx) => {
112 conf.address = addr[..idx].parse()?;
113 conf.net_range = addr[idx+1..].parse()?;
114 }
115 None => {
116 return Err(Box::new(WgMgrError::new(String::from("'Interface' > 'Address' is missing a subnet"))));
117 }
118 }
119 }
120 }
121
122 else if in_peer {
123 if line.starts_with("AllowedIPs") {
124 let addr = config_value(line)?;
125 match addr.find("/") {
126 Some(idx) => {
127 current_peer.ip = addr[..idx].parse()?;
128 let net_range: i32 = addr[idx+1..].parse()?;
129
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 }
140 }
141 else if line.starts_with("PublicKey") {
142 current_peer.public_key = String::from(config_value(line)?);
143 }
144 }
145
146 prev_line = line;
147 }
148
149 if current_peer.ip != Ipv4Addr::UNSPECIFIED {
150 conf.peers.push(current_peer);
151 }
152
153 Ok(conf)
154 }
155}
156
157impl Peer {
158 pub fn private_key(&self) -> Result<String, Box<dyn Error>> {
159 // TODO: do not hardcode where private keys are stored
160
161 let pk_folder = String::from("./private_keys/");
162 let pk_path = join_paths(&[pk_folder, self.name.clone()])?;
163
164 let pk = read_to_string(pk_path)?;
165
166 Ok(pk)
167 }
168}
169
170fn config_value(line: &str) -> Result<&str, &str> {
171 match line.find("=") {
172 Some(i) => {
173 Ok(line[i+1..].trim())
174 },
175 None => {
176 Err("line does not seem to contain a key-value pair")
177 }
178 }
179} \ No newline at end of file
diff --git a/src/main.rs b/src/main.rs
new file mode 100644
index 0000000..028e0e2
--- /dev/null
+++ b/src/main.rs
@@ -0,0 +1,3 @@
1fn main() {
2 println!("{:?}", wgmgr::WireguardConfig::new("wg.conf").unwrap())
3}