swindon 0.5.2

An HTTP edge (frontend) server with smart websockets support
use std::fmt;
use std::net::IpAddr;

use quire::validate::{Structure, Sequence, Scalar};
use rustc_serialize::{Decodable, Decoder};

use intern::Network;

#[derive(Debug, PartialEq, Eq)]
pub struct NetworkList {
    list: Vec<Subnet>,
}

#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Subnet(IpAddr, u32);

#[derive(RustcDecodable, Debug, PartialEq, Eq)]
pub struct SourceIpAuthorizer {
    pub allowed_network: Network,
    pub forwarded_ip_header: Option<String>,
    pub accept_forwarded_headers_from: Option<Network>,
}

pub fn source_ip_authorizer_validator<'x>() -> Structure<'x> {
    Structure::new()
    .member("allowed_network", Scalar::new())
    .member("forwarded_ip_header", Scalar::new().optional())
    .member("accept_forwarded_headers_from", Scalar::new().optional())
}

pub fn validator<'x>() -> Sequence<'x> {
    Sequence::new(Scalar::new())
}

impl Decodable for NetworkList {
    fn decode<D: Decoder>(d: &mut D) -> Result<Self, D::Error> {
        d.read_seq(|d, num| {
            let mut result = Vec::new();
            for i in 0..num {
                result.push(d.read_seq_elt(i, |d| {
                    let item = d.read_str()?;
                    if let Some(pos) = item.find('/') {
                        let ip = item[..pos].parse::<IpAddr>()
                            .map_err(|e| d.error(&e.to_string()))?;
                        let mask = item[pos+1..].parse::<u32>()
                            .map_err(|e| d.error(&e.to_string()))?;
                        let max_mask = match ip {
                            IpAddr::V4(_) => 24,
                            IpAddr::V6(_) => 128,
                        };
                        if mask % 8 != 0 {
                            return Err(d.error("Subnet mask must \
                                be multiple of eight"));
                        }
                        if mask > max_mask {
                            return Err(d.error(
                                &format!("Mask must be {} at max", max_mask)));
                        }
                        Ok(Subnet(ip, mask))
                    } else {
                        let ip = item.parse::<IpAddr>()
                            .map_err(|e| d.error(&e.to_string()))?;
                        match ip {
                            IpAddr::V4(_) => Ok(Subnet(ip, 24)),
                            IpAddr::V6(_) => Ok(Subnet(ip, 128)),
                        }
                    }
                })?);
            }
            Ok(NetworkList {
                list: result,
            })
        })
    }
}

impl fmt::Display for Subnet {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}/{}", self.0, self.1)
    }
}

impl NetworkList {
    pub fn get_subnet(&self, ip: IpAddr) -> Option<&Subnet> {
        for item in &self.list {
            match (ip, item) {
                (IpAddr::V4(my), &Subnet(IpAddr::V4(net), msk)) => {
                    let bytes = (msk / 8) as usize;
                    if my.octets()[..bytes] == net.octets()[..bytes] {
                        return Some(item);
                    }
                }
                (IpAddr::V6(my), &Subnet(IpAddr::V6(net), msk)) => {
                    let bytes = (msk / 8) as usize;
                    if my.octets()[..bytes] == net.octets()[..bytes] {
                        return Some(item);
                    }
                }
                _ => {}
            }
        }
        return None;
    }
}