1use std::{collections::HashMap, fmt::Debug, net::SocketAddr};
2
3use ipnetwork::IpNetwork;
4
5use crate::{WgConfError, WgKey};
6
7pub const PEER_TAG: &'static str = "[Peer]";
9
10const PUBLIC_KEY: &'static str = "PublicKey";
12const ALLOWED_IPS: &'static str = "AllowedIPs";
13const ENDPOINT: &'static str = "Endpoint";
14const PRESHARED_KEY: &'static str = "PresharedKey";
15const PERSISTENT_KEEPALIVE: &'static str = "PersistentKeepalive";
16
17#[derive(Debug, Clone, PartialEq, Eq)]
18pub enum SocketAddrExt {
19 Socket(SocketAddr),
20 Domain(String)
21}
22
23impl std::str::FromStr for SocketAddrExt {
24 type Err = ();
25
26 fn from_str(input: &str) -> Result<Self, Self::Err> {
27 if let Ok(ip) = input.parse::<SocketAddr>() {
28 return Ok(Self::Socket(ip));
29 }
30 Ok(Self::Domain(input.to_string()))
31 }
32}
33
34impl ToString for SocketAddrExt {
35 fn to_string(&self) -> String {
36 match self {
37 SocketAddrExt::Socket(socket_addr) => socket_addr.to_string(),
38 SocketAddrExt::Domain(domain_name) => domain_name.clone(),
39 }
40 }
41}
42
43#[derive(Clone, PartialEq, Eq)]
45pub struct WgPeer {
46 pub(crate) public_key: WgKey,
47 pub(crate) allowed_ips: Vec<IpNetwork>,
48 pub(crate) endpoint: Option<SocketAddrExt>,
49 pub(crate) preshared_key: Option<WgKey>,
50 pub(crate) persistent_keepalive: Option<u16>,
51}
52
53impl Debug for WgPeer {
54 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
55 f.debug_struct("WgPeer")
56 .field("public_key", &self.public_key)
57 .field("allowed_ips", &self.allowed_ips)
58 .field("endpoint", &self.endpoint)
59 .field("preshared_key", &"***")
60 .field("persistent_keepalive", &self.persistent_keepalive)
61 .finish()
62 }
63}
64
65impl ToString for WgPeer {
66 fn to_string(&self) -> String {
67 let mut allowed_ips_raw = String::new();
68 for (i, ip) in self.allowed_ips.iter().enumerate() {
69 allowed_ips_raw += &ip.to_string();
70 if i != self.allowed_ips.len() - 1 {
71 allowed_ips_raw += ", ";
72 }
73 }
74
75 let endpoint = match &self.endpoint {
76 Some(val) => format!("\n{} = {}", ENDPOINT, val.to_string()),
77 None => "".to_string(),
78 };
79
80 let preshared_key = match &self.preshared_key {
81 Some(val) => format!("\n{} = {}", PRESHARED_KEY, val.to_string()),
82 None => "".to_string(),
83 };
84
85 let keepalive = match self.persistent_keepalive {
86 Some(val) => format!("\n{} = {}", PERSISTENT_KEEPALIVE, &val),
87 None => "".to_string(),
88 };
89
90 format!(
91 "{}
92{} = {}
93{} = {}{}{}{}
94",
95 PEER_TAG,
96 PUBLIC_KEY,
97 self.public_key.to_string(),
98 ALLOWED_IPS,
99 allowed_ips_raw,
100 endpoint,
101 preshared_key,
102 keepalive
103 )
104 }
105}
106
107impl WgPeer {
108 pub fn new(
110 public_key: WgKey,
111 allowed_ips: Vec<IpNetwork>,
112 endpoint: Option<SocketAddrExt>,
113 preshared_key: Option<WgKey>,
114 persistent_keepalive: Option<u16>,
115 ) -> WgPeer {
116 WgPeer {
117 public_key,
118 allowed_ips,
119 endpoint,
120 preshared_key,
121 persistent_keepalive,
122 }
123 }
124
125 pub fn from_raw_values(
126 public_key: String,
127 allowed_ips: Vec<String>,
128 endpoint: Option<String>,
129 preshared_key: Option<String>,
130 persistent_keepalive: Option<String>,
131 ) -> Result<WgPeer, WgConfError> {
132 let public_key: WgKey = public_key.parse()?;
133
134 let allowed_ips: Result<Vec<IpNetwork>, WgConfError> = allowed_ips
135 .iter()
136 .map(|ip| {
137 ip.parse::<IpNetwork>().map_err(|_| {
138 WgConfError::ValidationFailed(
139 "allowed IPs must be addresses with mask (e.g. 10.0.0.1/8)".to_string(),
140 )
141 })
142 })
143 .collect();
144 let allowed_ips = allowed_ips?;
145
146 let endpoint: Option<SocketAddrExt> = endpoint
147 .map(|endpoint| {
148 endpoint.parse().map_err(|_| {
149 WgConfError::ValidationFailed("invalid endpoint raw value".to_string())
150 })
151 })
152 .transpose()?;
153
154 let preshared_key: Option<WgKey> = preshared_key
155 .map(|key| {
156 key.parse().map_err(|_| {
157 WgConfError::ValidationFailed("invalid preshared key raw value".to_string())
158 })
159 })
160 .transpose()?;
161
162 let persistent_keepalive: Option<u16> = persistent_keepalive
163 .map(|p| {
164 p.parse().map_err(|_| {
165 WgConfError::ValidationFailed(
166 "invalid persistent keepalive raw value".to_string(),
167 )
168 })
169 })
170 .transpose()?;
171
172 Ok(WgPeer::new(
173 public_key,
174 allowed_ips,
175 endpoint,
176 preshared_key,
177 persistent_keepalive,
178 ))
179 }
180
181 pub fn public_key(&self) -> &WgKey {
183 &self.public_key
184 }
185 pub fn allowed_ips(&self) -> &[IpNetwork] {
186 &self.allowed_ips
187 }
188 pub fn endpoint(&self) -> Option<&SocketAddrExt> {
189 self.endpoint.as_ref()
190 }
191 pub fn preshared_key(&self) -> Option<&WgKey> {
192 self.preshared_key.as_ref()
193 }
194 pub fn persistent_keepalive(&self) -> Option<u16> {
195 self.persistent_keepalive
196 }
197
198 pub(crate) fn from_raw_key_values(
199 raw_key_values: HashMap<String, String>,
200 ) -> Result<WgPeer, WgConfError> {
201 let mut public_key = String::new();
202 let mut allowed_ips = Vec::<String>::new();
203 let mut endpoint: Option<String> = None;
204 let mut preshared_key: Option<String> = None;
205 let mut persistent_keepalive: Option<String> = None;
206
207 for (k, v) in raw_key_values {
208 match k {
209 _ if k == PUBLIC_KEY => public_key = v,
210 _ if k == ALLOWED_IPS => {
211 let ips: Vec<&str> = v.split(", ").collect();
212 for ip in ips {
213 allowed_ips.push(ip.to_string());
214 }
215 }
216 _ if k == ENDPOINT => endpoint = Some(v),
217 _ if k == PRESHARED_KEY => preshared_key = Some(v),
218 _ if k == PERSISTENT_KEEPALIVE => persistent_keepalive = Some(v),
219 _ => continue,
220 }
221 }
222
223 WgPeer::from_raw_values(
224 public_key,
225 allowed_ips,
226 endpoint,
227 preshared_key,
228 persistent_keepalive,
229 )
230 }
231}
232
233#[cfg(test)]
234mod tests {
235 use super::*;
236
237 #[test]
238 fn wg_peer_0_to_string_0_all_fields() {
239 let peer = WgPeer::new(
241 "6FyM4Sq5zanp+9UPXIygLJQBYvlLsfF5lYcrSoa3CX8="
242 .parse()
243 .unwrap(),
244 vec![
245 "10.0.0.1/32".parse().unwrap(),
246 "10.0.0.2/32".parse().unwrap(),
247 ],
248 Some("127.0.0.2:8080".parse().unwrap()),
249 Some(
250 "6FyM4Sq5zanp+9UOXIygLJQBYvlLsfF5lYcrSoa3CX8="
251 .parse()
252 .unwrap(),
253 ),
254 Some(25),
255 );
256
257 let peer_raw = peer.to_string();
259
260 assert_eq!(
262 "[Peer]
263PublicKey = 6FyM4Sq5zanp+9UPXIygLJQBYvlLsfF5lYcrSoa3CX8=
264AllowedIPs = 10.0.0.1/32, 10.0.0.2/32
265Endpoint = 127.0.0.2:8080
266PresharedKey = 6FyM4Sq5zanp+9UOXIygLJQBYvlLsfF5lYcrSoa3CX8=
267PersistentKeepalive = 25
268",
269 peer_raw
270 );
271 }
272
273 #[test]
274 fn wg_peer_0_to_string_0_single_ip() {
275 let peer = WgPeer::new(
277 "6FyM4Sq5zanp+9UPXIygLJQBYvlLsfF5lYcrSoa3CX8="
278 .parse()
279 .unwrap(),
280 vec!["10.0.0.1/32".parse().unwrap()],
281 Some("127.0.0.2:8080".parse().unwrap()),
282 Some(
283 "6FyM4Sq5zanp+9UOXIygLJQBYvlLsfF5lYcrSoa3CX8="
284 .parse()
285 .unwrap(),
286 ),
287 Some(25),
288 );
289
290 let peer_raw = peer.to_string();
292
293 assert_eq!(
295 "[Peer]
296PublicKey = 6FyM4Sq5zanp+9UPXIygLJQBYvlLsfF5lYcrSoa3CX8=
297AllowedIPs = 10.0.0.1/32
298Endpoint = 127.0.0.2:8080
299PresharedKey = 6FyM4Sq5zanp+9UOXIygLJQBYvlLsfF5lYcrSoa3CX8=
300PersistentKeepalive = 25
301",
302 peer_raw
303 );
304 }
305
306 #[test]
307 fn wg_peer_0_to_string_0_empty_optionals() {
308 let peer = WgPeer::new(
310 "6FyM4Sq5zanp+9UPXIygLJQBYvlLsfF5lYcrSoa3CX8="
311 .parse()
312 .unwrap(),
313 vec![
314 "10.0.0.1/32".parse().unwrap(),
315 "10.0.0.2/32".parse().unwrap(),
316 ],
317 None,
318 None,
319 None,
320 );
321
322 let peer_raw = peer.to_string();
324
325 assert_eq!(
327 "[Peer]
328PublicKey = 6FyM4Sq5zanp+9UPXIygLJQBYvlLsfF5lYcrSoa3CX8=
329AllowedIPs = 10.0.0.1/32, 10.0.0.2/32
330",
331 peer_raw
332 );
333 }
334}