wireguard_conf/models/
peer.rs

1use derive_builder::Builder;
2use either::Either;
3use ipnet::IpNet;
4
5use std::fmt;
6use std::net::{IpAddr, Ipv6Addr};
7use std::{convert::Infallible, net::Ipv4Addr};
8
9#[cfg(feature = "serde")]
10#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
11use serde::{Deserialize, Serialize};
12
13use crate::prelude::*;
14
15/// Options for [`Peer::to_interface()`].
16#[derive(Clone, Copy, Debug, PartialEq, Default)]
17#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
18pub struct ToInterfaceOptions {
19    /// Option, for setting server as default gateway.
20    default_gateway: bool,
21
22    /// Option, for setting persistent keepalive to client's peer.
23    persistent_keepalive: u16,
24}
25
26impl ToInterfaceOptions {
27    /// Create new [`ToInterfaceOptions`].
28    #[must_use]
29    pub fn new() -> Self {
30        Self::default()
31    }
32
33    /// Sets server as default gateway.
34    ///
35    /// When client interface will be generated, it will set `client_interface.peers[0].allowed_ips` to `0.0.0.0/0`
36    #[must_use]
37    pub fn default_gateway(mut self, value: bool) -> Self {
38        self.default_gateway = value;
39        self
40    }
41
42    /// Sets persistent keepalive to client's peer.
43    ///
44    #[must_use]
45    pub fn persistent_keepalive(mut self, value: u16) -> Self {
46        self.persistent_keepalive = value;
47        self
48    }
49}
50
51/// Struct, that represents `[Peer]` section in configuration.
52///
53/// [Wireguard docs](https://github.com/pirate/wireguard-docs?tab=readme-ov-file#peer)
54#[must_use]
55#[derive(Clone, Debug, PartialEq, Builder)]
56#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
57#[builder(build_fn(private, name = "fallible_build", error = "Infallible"))]
58pub struct Peer {
59    /// Peer's endpoint.
60    ///
61    /// [Wireguard Docs](https://github.com/pirate/wireguard-docs?tab=readme-ov-file#endpoint)
62    #[builder(setter(into, strip_option), default)]
63    pub endpoint: Option<String>,
64
65    /// Peer's allowed IPs.
66    ///
67    /// - */32 and */128 ipnets will be generated as regular ips (f.e. 1.2.3.4/32 -> 1.2.3.4)
68    ///
69    /// [Wireguard Docs](https://github.com/pirate/wireguard-docs?tab=readme-ov-file#allowedips)
70    #[builder(setter(into), default)]
71    pub allowed_ips: Vec<IpNet>,
72
73    /// Peer's persistent keepalive.
74    ///
75    /// Represents in seconds how often to send an authenticated empty packet to the peer, for the
76    /// purpose of keeping a stateful firewall or NAT mapping valid persistently.
77    ///
78    /// Setting this value to `0` omits it in config.
79    ///
80    /// [Wireguard docs](https://github.com/pirate/wireguard-docs?tab=readme-ov-file#persistentkeepalive)
81    #[builder(default)]
82    pub persistent_keepalive: u16,
83
84    /// Peer's key.
85    ///
86    /// If [`PrivateKey`] is provided, then peer can be exported to interface & full config.
87    /// Otherwise only to peer section of config.
88    #[builder(default = Either::Left(PrivateKey::random()))]
89    pub key: Either<PrivateKey, PublicKey>,
90
91    /// Peer's preshared-key.
92    #[builder(setter(strip_option), default)]
93    pub preshared_key: Option<PresharedKey>,
94
95    /// AmneziaWG settings.
96    ///
97    /// Used for packet obfuscation.
98    #[cfg(feature = "amneziawg")]
99    #[cfg_attr(docsrs, doc(cfg(feature = "amneziawg")))]
100    #[builder(setter(strip_option), default)]
101    pub amnezia_settings: Option<AmneziaSettings>,
102}
103
104impl Peer {
105    /// Create new `PeerBuilder`. Alias for `PeerBuilder::new()`.
106    ///
107    /// ```rust
108    /// # use wireguard_conf::prelude::*;
109    /// # use wireguard_conf::as_ipnet;
110    /// #
111    /// let interface = Peer::builder()
112    ///     .allowed_ips([as_ipnet!("0.0.0.0/0")])
113    ///     // <snip>
114    ///     .build();
115    /// ```
116    #[must_use]
117    pub fn builder() -> PeerBuilder {
118        PeerBuilder::default()
119    }
120}
121
122impl PeerBuilder {
123    /// Create new `PeerBuilder`.
124    ///
125    /// ```rust
126    /// # use wireguard_conf::prelude::*;
127    /// # use wireguard_conf::as_ipnet;
128    /// #
129    /// let interface = PeerBuilder::new()
130    ///     .allowed_ips([as_ipnet!("0.0.0.0/0")])
131    ///     // <snip>
132    ///     .build();
133    /// ```
134    #[must_use]
135    pub fn new() -> Self {
136        Self::default()
137    }
138
139    /// Sets private key.
140    ///
141    /// Shorthand for `.key(Either::Left(value))`.
142    pub fn private_key(&mut self, value: PrivateKey) -> &mut Self {
143        self.key = Some(Either::Left(value));
144        self
145    }
146
147    /// Sets public key.
148    ///
149    /// Shorthand for `.key(Either::Right(value))`.
150    pub fn public_key(&mut self, value: PublicKey) -> &mut Self {
151        self.key = Some(Either::Right(value));
152        self
153    }
154
155    /// Builds an `Interface`.
156    pub fn build(&self) -> Peer {
157        self.fallible_build().unwrap_or_else(|_| unreachable!())
158    }
159}
160
161impl Peer {
162    /// Generate [`Interface`] from client's [`Peer`] and server's [`Interface`].
163    ///
164    /// `options`
165    ///
166    /// # Errors
167    ///
168    /// - [`WireguardError::NoPrivateKeyProvided`] -- peer don't have private key.
169    ///   You need to provide [`PrivateKey`] for creating interfaces from peers.
170    /// - [`WireguardError::NoAssignedIP`] -- no assigned ip found.
171    ///   This means that your peer doesn't have allowed ip, that is in interface's addresses
172    ///   network.
173    pub fn to_interface(
174        &self,
175        server_interface: &Interface,
176        options: ToInterfaceOptions,
177    ) -> WireguardResult<Interface> {
178        let Either::Left(private_key) = self.key.clone() else {
179            return Err(WireguardError::NoPrivateKeyProvided);
180        };
181
182        let assigned_ips: Vec<IpNet> = self
183            .allowed_ips
184            .iter()
185            .filter_map(|allowed_ip| {
186                for server_address in &server_interface.address {
187                    if server_address.contains(allowed_ip) {
188                        return IpNet::new(allowed_ip.addr(), server_address.prefix_len()).ok();
189                    }
190                }
191
192                None
193            })
194            .collect();
195
196        if assigned_ips.is_empty() {
197            return Err(WireguardError::NoAssignedIP);
198        }
199
200        let mut client_interface = Interface {
201            endpoint: None,
202
203            address: assigned_ips.clone(),
204            listen_port: None,
205            private_key,
206            dns: server_interface.dns.clone(),
207
208            table: None,
209            mtu: None,
210
211            #[cfg(feature = "amneziawg")]
212            amnezia_settings: self.amnezia_settings.clone(),
213
214            pre_up: vec![],
215            pre_down: vec![],
216            post_up: vec![],
217            post_down: vec![],
218
219            peers: vec![server_interface.to_peer()],
220        };
221
222        if options.default_gateway {
223            client_interface.peers[0].allowed_ips = {
224                let mut allowed_ips = Vec::with_capacity(1);
225
226                if assigned_ips.iter().any(|ip| ip.addr().is_ipv4()) {
227                    allowed_ips.push(IpNet::new_assert(IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0));
228                }
229
230                if assigned_ips.iter().any(|ip| ip.addr().is_ipv6()) {
231                    allowed_ips.push(IpNet::new_assert(IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0));
232                }
233
234                allowed_ips
235            };
236        }
237
238        if options.persistent_keepalive != 0 {
239            client_interface.peers[0].persistent_keepalive = options.persistent_keepalive;
240        }
241
242        Ok(client_interface)
243    }
244}
245
246/// Implements [`fmt::Display`] for exporting peer.
247///
248/// # Note
249///
250/// It exports only `[Peer] ...` part. To export full interface, use [`Peer::to_interface()`]
251/// and then `.to_string()`
252impl fmt::Display for Peer {
253    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
254        writeln!(f, "[Peer]")?;
255        if let Some(endpoint) = self.endpoint.clone() {
256            writeln!(f, "Endpoint = {endpoint}")?;
257        }
258        writeln!(
259            f,
260            "AllowedIPs = {}",
261            self.allowed_ips
262                .iter()
263                .map(std::string::ToString::to_string)
264                .collect::<Vec<String>>()
265                .join(",")
266        )?;
267        writeln!(
268            f,
269            "PublicKey = {}",
270            self.key.clone().right_or_else(|key| PublicKey::from(&key))
271        )?;
272        if let Some(preshared_key) = &self.preshared_key {
273            writeln!(f, "PresharedKey = {preshared_key}")?;
274        }
275        if self.persistent_keepalive != 0 {
276            writeln!(f, "PersistentKeepalive = {}", self.persistent_keepalive)?;
277        }
278
279        Ok(())
280    }
281}