aboutsummaryrefslogblamecommitdiff
path: root/src/manager/builder.rs
blob: 9c2ad1556cd912eb55f4483bd8d8f233e43c92aa (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                                             
  
                              
 
                  
                                  
                               
                      

                

                    
                         
                    


                      

                                                                                       
              

                                  



                      




                                     

 

                              
                                                              

               
                               
                               
                              
                    
                                    
              
                     
                      




                    





                                      
                                     
                     
                    
                           
                                 



                            
                                                                              
              
                                      

                        
               



             
                                                       



                          


                                                                   
                                                                





                                 


                                                
 



                                                 
                                                                                 
                                                  


             
                                                                              
                                                                 






                                 
                                                 
                                     
                                                          
                    


                        



                                                
                                                                          

                                                                 
                
                    
                                                                           
                   
          
                                                  





                             

                         
                    
                             
                          
                                       
                                           
                                                                       

                          






                                                                


     
                    

                                 
                    




                                                 

      
















                                                                                


         
         


                                                                                         


                            
                      
                                        





                              
                      
                                        








                              
                              
                
                             
          
                                                  

     
// 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.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<model::Endpoint>,
    psk: Option<&'a model::Secret>,
    keepalive: u32,
}

pub(super) struct ConfigBuilder<'a> {
    c: model::Config,
    err: Vec<Error>,
    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<Error>) {
        (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<Error>,
    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<PeerContact<'a>, 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.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<Error>, 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));
    }
}