aboutsummaryrefslogtreecommitdiff
path: root/src/manager/builder.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/manager/builder.rs')
-rw-r--r--src/manager/builder.rs171
1 files changed, 171 insertions, 0 deletions
diff --git a/src/manager/builder.rs b/src/manager/builder.rs
new file mode 100644
index 0000000..9fc2291
--- /dev/null
+++ b/src/manager/builder.rs
@@ -0,0 +1,171 @@
+// Copyright 2019 Hristo Venev
+//
+// See COPYING.
+
+use crate::{config, model, proto};
+use std::collections::hash_map;
+use std::{error, fmt};
+
+#[derive(Debug)]
+pub struct ConfigError {
+ pub url: String,
+ pub peer: model::Key,
+ pub important: bool,
+ err: &'static str,
+}
+
+impl ConfigError {
+ fn new(err: &'static str, s: &config::Source, p: &proto::Peer, important: bool) -> Self {
+ Self {
+ url: s.url.clone(),
+ peer: p.public_key.clone(),
+ important,
+ err,
+ }
+ }
+}
+
+impl error::Error for ConfigError {}
+impl fmt::Display for ConfigError {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ write!(
+ f,
+ "{} [{}] from [{}]: {}",
+ if self.important {
+ "Invalid peer"
+ } else {
+ "Misconfigured peer"
+ },
+ self.peer,
+ self.url,
+ self.err
+ )
+ }
+}
+
+pub struct ConfigBuilder<'a> {
+ c: model::Config,
+ err: Vec<ConfigError>,
+ public_key: model::Key,
+ pc: &'a config::PeerConfig,
+}
+
+impl<'a> ConfigBuilder<'a> {
+ #[inline]
+ pub fn new(public_key: model::Key, pc: &'a config::PeerConfig) -> Self {
+ Self {
+ c: model::Config::default(),
+ err: vec![],
+ public_key,
+ pc,
+ }
+ }
+
+ #[inline]
+ pub fn build(self) -> (model::Config, Vec<ConfigError>) {
+ (self.c, self.err)
+ }
+
+ #[inline]
+ pub fn add_server(&mut self, s: &config::Source, p: &proto::Server) {
+ if p.peer.public_key == self.public_key {
+ return;
+ }
+
+ let pc = self.pc;
+ let ent = insert_peer(&mut self.c, &mut self.err, s, &p.peer, |ent| {
+ ent.psk = s.psk.clone();
+ ent.endpoint = Some(p.endpoint.clone());
+ ent.keepalive = pc.fix_keepalive(p.keepalive);
+ });
+
+ add_peer(&mut self.err, ent, s, &p.peer)
+ }
+
+ #[inline]
+ pub fn add_road_warrior(&mut self, s: &config::Source, p: &proto::RoadWarrior) {
+ if p.peer.public_key == self.public_key {
+ self.err.push(ConfigError::new(
+ "The local peer cannot be a road warrior",
+ s,
+ &p.peer,
+ true,
+ ));
+ return;
+ }
+
+ let ent = if p.base == self.public_key {
+ insert_peer(&mut self.c, &mut self.err, s, &p.peer, |_| {})
+ } else if let Some(ent) = self.c.peers.get_mut(&p.base) {
+ ent
+ } else {
+ self.err
+ .push(ConfigError::new("Unknown base peer", s, &p.peer, true));
+ return;
+ };
+ add_peer(&mut self.err, ent, s, &p.peer)
+ }
+}
+
+#[inline]
+fn insert_peer<'b>(
+ c: &'b mut model::Config,
+ err: &mut Vec<ConfigError>,
+ s: &config::Source,
+ p: &proto::Peer,
+ update: impl for<'c> FnOnce(&'c mut model::Peer) -> (),
+) -> &'b mut model::Peer {
+ match c.peers.entry(p.public_key.clone()) {
+ hash_map::Entry::Occupied(ent) => {
+ err.push(ConfigError::new("Duplicate public key", s, p, true));
+ ent.into_mut()
+ }
+ hash_map::Entry::Vacant(ent) => {
+ let ent = ent.insert(model::Peer {
+ endpoint: None,
+ psk: None,
+ keepalive: 0,
+ ipv4: vec![],
+ ipv6: vec![],
+ });
+ update(ent);
+ ent
+ }
+ }
+}
+
+fn add_peer(
+ err: &mut Vec<ConfigError>,
+ ent: &mut model::Peer,
+ s: &config::Source,
+ p: &proto::Peer,
+) {
+ let mut added = false;
+ let mut removed = false;
+
+ for i in &p.ipv4 {
+ if s.ipv4.contains(i) {
+ ent.ipv4.push(*i);
+ added = true;
+ } else {
+ removed = true;
+ }
+ }
+ for i in &p.ipv6 {
+ if s.ipv6.contains(i) {
+ ent.ipv6.push(*i);
+ added = true;
+ } else {
+ removed = true;
+ }
+ }
+
+ if removed {
+ let msg = if added {
+ "Some IPs removed"
+ } else {
+ "All IPs removed"
+ };
+ err.push(ConfigError::new(msg, s, p, !added));
+ }
+}