aboutsummaryrefslogblamecommitdiff
path: root/src/builder.rs
blob: ba3843bb8572392cc40fe15c976c273d910de529 (plain) (tree)
1
2
3
4
5
6
7




                                  
                               
                      





























































                                                                                             
                                                                         














                                                                             
                                                                                    
                                                 





                                                          








                                                                       

                                                                                       


































                                                                           





                               




























                                                      
// 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 {
        ConfigError {
            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 {
        ConfigBuilder {
            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 {
            match self.c.peers.get_mut(&p.base) {
                Some(ent) => ent,
                None => {
                    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.iter() {
        if s.ipv4.contains(i) {
            ent.ipv4.push(*i);
            added = true;
        } else {
            removed = true;
        }
    }
    for i in p.ipv6.iter() {
        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));
    }
}