// SPDX-License-Identifier: LGPL-3.0-or-later // // Copyright 2019 Hristo Venev use super::Source; use crate::{config, model, proto}; use std::collections::hash_map; use std::{error, fmt}; #[derive(Debug)] pub struct Error { pub src: String, pub peer: model::Key, important: bool, err: &'static str, } impl Error { fn new(err: &'static str, src: &Source, p: &proto::Peer, important: bool) -> Self { Self { src: src.config.name.clone(), peer: p.public_key, important, err, } } #[inline] pub fn important(&self) -> bool { self.important } } impl error::Error for Error {} impl fmt::Display for Error { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!( f, "{} [{}]/[{}]: {}", if self.important { "invalid peer" } else { "misconfigured peer" }, self.src, self.peer, self.err ) } } struct PeerContact<'a> { endpoint: Option, psk: Option<&'a model::Secret>, keepalive: u32, } pub(super) struct ConfigBuilder<'a> { c: model::Config, err: Vec, public_key: model::Key, gc: &'a config::GlobalConfig, } impl<'a> ConfigBuilder<'a> { #[inline] pub fn new(public_key: model::Key, gc: &'a config::GlobalConfig) -> Self { Self { c: model::Config::empty(), err: vec![], public_key, gc, } } #[inline] pub fn build(self) -> (model::Config, Vec) { (self.c, self.err) } #[inline] pub fn add_server(&mut self, src: &Source, p: &proto::Server) { let gc = self.gc; let mut contact = match peer_contact(gc, src, &p.peer) { Ok(v) => v, Err(e) => { self.err.push(e); return; } }; if contact.endpoint.is_none() { contact.endpoint = Some(p.endpoint); } if p.peer.public_key == self.public_key { return; } let ent = insert_peer(&mut self.c, &mut self.err, src, &p.peer, contact); add_peer(&mut self.err, ent, src, &p.peer) } #[inline] pub fn add_road_warrior(&mut self, src: &Source, p: &proto::RoadWarrior) { let contact = match peer_contact(self.gc, src, &p.peer) { Ok(v) => v, Err(e) => { self.err.push(e); return; } }; if p.peer.public_key == self.public_key { self.err.push(Error::new( "the local peer cannot be a road warrior", src, &p.peer, true, )); return; } let ent = if p.base == self.public_key { insert_peer(&mut self.c, &mut self.err, src, &p.peer, contact) } else if let Some(ent) = self.c.peers.get_mut(&p.base) { ent } else { self.err .push(Error::new("unknown base peer", src, &p.peer, true)); return; }; add_peer(&mut self.err, ent, src, &p.peer) } } #[inline] fn insert_peer<'b>( c: &'b mut model::Config, err: &mut Vec, src: &Source, p: &proto::Peer, contact: PeerContact<'_>, ) -> &'b mut model::Peer { match c.peers.entry(p.public_key) { hash_map::Entry::Occupied(ent) => { err.push(Error::new("duplicate public key", src, p, true)); ent.into_mut() } hash_map::Entry::Vacant(ent) => ent.insert(model::Peer { endpoint: contact.endpoint, psk: contact.psk.cloned(), keepalive: contact.keepalive, ipv4: vec![], ipv6: vec![], }), } } fn peer_contact<'a>( gc: &'a config::GlobalConfig, src: &'a Source, p: &proto::Peer, ) -> Result, Error> { let mut r = PeerContact { psk: src.config.psk.as_ref(), endpoint: None, keepalive: gc.fix_keepalive(p.keepalive), }; if let Some(pc) = gc.peers.get(&p.public_key) { if let Some(ref want_src) = &pc.source { if *want_src != src.config.name { return Err(Error::new("peer source not allowed", src, p, true)); } } if let Some(endpoint) = pc.endpoint { r.endpoint = Some(endpoint); } if let Some(ref psk) = &pc.psk { r.psk = Some(psk); } if let Some(keepalive) = pc.keepalive { r.keepalive = keepalive; } } Ok(r) } fn add_peer(err: &mut Vec, ent: &mut model::Peer, src: &Source, p: &proto::Peer) { let mut added = false; let mut removed = false; for i in &p.ipv4 { if src.config.ipv4.contains(i) { ent.ipv4.push(*i); added = true; } else { removed = true; } } for i in &p.ipv6 { if src.config.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(Error::new(msg, src, p, !added)); } }