<commit_before>use crate::database::*;
use crate::wires::*;
use crate::bels::*;

use std::collections::{BTreeSet, BTreeMap};
use std::fmt;

#[derive(Clone)]
pub struct SitePin {
    pub tile_wire: String,
    pub site_wire: String,
    pub dir: PinDir,
    pub bel_pin: usize,
}

impl fmt::Debug for SitePin {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {} {}",
            &self.tile_wire,
            match self.dir {
                PinDir::INPUT => "-->",
                PinDir::OUTPUT => "<--",
                PinDir::INOUT => "<->",
            },
            &self.site_wire
        )
    }
}

#[derive(Clone)]
pub struct SiteRoutingBel {
    pub src_wires: Vec<String>,
    pub dst_wire: String,
}

impl fmt::Debug for SiteRoutingBel {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} <-- {{", &self.dst_wire)?;
        let mut first = true;
        for src in self.src_wires.iter() {
            if !first { write!(f, ", ")?; }
            write!(f, "{}", src)?;
            first = false;
        }
        write!(f, "}}")?;
        Ok(())
    }
}

#[derive(Clone)]
pub struct SiteBelPin {
    pub bel_name: String,
    pub pin_name: String,
    pub site_wire: String,
    pub dir: PinDir,
}

impl fmt::Debug for SiteBelPin {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {} {}",
            &self.site_wire,
            match self.dir {
                PinDir::INPUT => "-->",
                PinDir::OUTPUT => "<--",
                PinDir::INOUT => "<->",
            },
            &self.pin_name
        )
    }
}

#[derive(Clone, Eq, PartialEq)]
pub enum SiteBelClass {
    BEL,
    RBEL,
    PORT,
}

#[derive(Clone)]
pub struct SiteBel {
    pub name: String,
    pub bel_type: String,
    pub bel_class: SiteBelClass,
    pub pins: Vec<usize>,
}

#[derive(Clone)]
pub struct SiteWire {
    pub name: String,
    pub bel_pins: Vec<usize>,
}

#[derive(Clone)]
pub struct SitePIP {
    pub in_pin: usize,
    pub out_pin: usize,
}

#[derive(Clone)]
pub struct Site {
    pub name: String,
    pub site_type: String,
    pub wires: Vec<SiteWire>,
    pub bel_pins: Vec<SiteBelPin>,
    pub pins: Vec<SitePin>,
    pub last_input_pin: usize,
    pub bels: Vec<SiteBel>,
    pub pips: Vec<SitePIP>,
}

// To speed up site routing; where fixed connections connect multiple site wires together, merge them into one
// this structure stores the mapping in both directions
struct SiteWireMap {
    pub wire2root: BTreeMap<String, String>,
    pub root2wires: BTreeMap<String, BTreeSet<String>>,
}

impl SiteWireMap {
    pub fn lookup_wire(&self, name: &str) -> String {
        self.wire2root.get(name).unwrap_or(&name.to_string()).to_string()
    }
    pub fn add_alias(&mut self, root: &str, wire: &str) {
        self.wire2root.entry(wire.to_string()).or_insert(root.to_string());
        self.root2wires.entry(root.to_string()).or_insert(BTreeSet::new()).insert(wire.to_string());
        if let Some(wires) = self.root2wires.get(wire).cloned() {
            for w in wires {
                *self.wire2root.get_mut(&w).unwrap() = root.to_string();
                self.root2wires.get_mut(root).unwrap().insert(w.to_string());
            }
            self.root2wires.remove(wire);
        }
    }
}

// determine which wire has priority when flattening connections
fn compare_wire(a: &str, b: &str) -> bool {
    // prioritise shorter wires first, then fall back to a lexicographical compare
    (a.len(), a) < (b.len(), b)
}

fn flatten_wires(tiletype: &str, tiledata: &TileBitsDatabase) -> SiteWireMap {
    let mut m = SiteWireMap {
        wire2root: BTreeMap::new(),
        root2wires: BTreeMap::new(),
    };
    for (dst_wire, conns) in tiledata.conns.iter() {
        if !is_site_wire(tiletype, dst_wire) {
            continue;
        }
        for c in conns {
            let looked_up_src = m.lookup_wire(&c.from_wire);
            let looked_up_dst = m.lookup_wire(dst_wire);
            if compare_wire(&looked_up_src, &looked_up_dst) && is_site_wire(tiletype, &c.from_wire) {
                m.add_alias(&looked_up_src, &looked_up_dst);
            } else {
                m.add_alias(&looked_up_dst, &looked_up_src);
            }
        }
    }
    return m;
}

pub fn build_sites(tiletype: &str, tiledata: &TileBitsDatabase) -> Vec<Site> {
    // TODO: handle other tile types
    let mut sites = Vec::new();
    if tiletype == "PLC" {
        let flat_wires = flatten_wires(tiletype, tiledata);
        let mut pins = Vec::new();
        let mut found_pins = BTreeSet::new();
        for (dst_wire, conns) in tiledata.conns.iter() {
            for conn in conns.iter() {
                if is_site_wire(tiletype, dst_wire) && !is_site_wire(tiletype, &conn.from_wire) {
                    // from tile into site
                    let site_dst_wire = flat_wires.lookup_wire(dst_wire);
                    if found_pins.contains(&(conn.from_wire.to_string(), site_dst_wire.to_string())) {
                        continue;
                    }
                    found_pins.insert((conn.from_wire.to_string(), site_dst_wire.to_string()));
                    pins.push(SitePin {
                        tile_wire: conn.from_wire.clone(),
                        site_wire: site_dst_wire,
                        dir: PinDir::INPUT,
                        bel_pin: 0,
                    });
                } else if !is_site_wire(tiletype, dst_wire) && is_site_wire(tiletype, &conn.from_wire) {
                    // from site into tile
                    let site_src_wire = flat_wires.lookup_wire(&conn.from_wire);
                    if found_pins.contains(&(site_src_wire.to_string(), dst_wire.to_string())) {
                        continue;
                    }
                    found_pins.insert((site_src_wire.to_string(), dst_wire.to_string()));
                    pins.push(SitePin {
                        tile_wire: dst_wire.to_string(),
                        site_wire: site_src_wire,
                        dir: PinDir::OUTPUT,
                        bel_pin: 0,
                    });
                }
            }
        }
        // For the F outputs where have a MUX/LUT choice; use the FF DI mux because it's not independently controllable from the FF mux
        for i in 0..4 {
                pins.push(SitePin {
                    tile_wire: format!("F{}", 2*i),
                    site_wire: flat_wires.lookup_wire(&format!("JDI0_SLICE{}", &"ABCD"[i..i+1])),
                    dir: PinDir::OUTPUT,
                    bel_pin: 0,
                });
        }
        let mut site_bel_pins = Vec::new();
        let mut site_bels = Vec::new();
        let mut site_pips = Vec::new();
        // Import functional bels
        let orig_bels = get_tile_bels(&tiletype, tiledata);
        for orig_bel in orig_bels.iter() {
            let mut bel_pins = Vec::new();

            for pin in &orig_bel.pins {
                // TODO: relative X and Y coordinates
                let wire_name = pin.wire.rel_name(orig_bel.rel_x, orig_bel.rel_y);
                assert!(is_site_wire(tiletype, &wire_name));
                let site_wire = flat_wires.lookup_wire(&wire_name);
                bel_pins.push(site_bel_pins.len());
                site_bel_pins.push(SiteBelPin {
                    bel_name: orig_bel.name.clone(),
                    pin_name: pin.name.clone(),
                    site_wire: site_wire,
                    dir: pin.dir.clone(),
                });
            }

            site_bels.push(SiteBel {
                name: orig_bel.name.clone(),
                bel_class: SiteBelClass::BEL,
                bel_type: orig_bel.beltype.clone(),
                pins: bel_pins,
            });
        }
        // Convert pips to routing bels
        for (dst_wire, pips) in tiledata.pips.iter() {
            if !is_site_wire(tiletype, dst_wire) {
                continue;
            }
            let mut bel_pins = Vec::new();
            let site_dst_wire = flat_wires.lookup_wire(dst_wire);
            let bel_name = format!("RBEL_{}", site_dst_wire);
            let out_pin_idx = site_bel_pins.len();
            bel_pins.push(out_pin_idx);
            site_bel_pins.push(SiteBelPin {
                bel_name: bel_name.clone(),
                pin_name: site_dst_wire.clone(),
                site_wire: site_dst_wire.clone(),
                dir: PinDir::OUTPUT,
            });
            for src_wire in pips.iter().map(|p| &p.from_wire).filter(|w| is_site_wire(tiletype, w)).map(|w| flat_wires.lookup_wire(w)) {
                site_pips.push(SitePIP {
                    in_pin: site_bel_pins.len(),
                    out_pin: out_pin_idx,
                });
                bel_pins.push(site_bel_pins.len());
                site_bel_pins.push(SiteBelPin {
                    bel_name: bel_name.clone(),
                    pin_name: src_wire.clone(),
                    site_wire: src_wire.clone(),
                    dir: PinDir::INPUT,
                });
            }

            site_bels.push(SiteBel {
                name: bel_name.clone(),
                bel_class: SiteBelClass::RBEL,
                bel_type: bel_name.clone(),
                pins: bel_pins,
            });
        }
        // Create port bels
        for pin in pins.iter_mut() {
            let bel_name = pin.site_wire.clone();
            let bel_pins = vec![site_bel_pins.len()];
            pin.bel_pin = site_bel_pins.len();
            site_bel_pins.push(SiteBelPin {
                bel_name: bel_name.clone(),
                pin_name: pin.site_wire.clone(),
                site_wire: pin.site_wire.clone(),
                dir: match pin.dir {
                    PinDir::INPUT => PinDir::OUTPUT,
                    PinDir::OUTPUT => PinDir::INPUT,
                    PinDir::INOUT => PinDir::INOUT,
                }
            });
            site_bels.push(SiteBel {
                name: bel_name.clone(),
                bel_class: SiteBelClass::PORT,
                bel_type: bel_name.clone(),
                pins: bel_pins,
            })
        }

        let mut site_wire_to_pins = BTreeMap::new();
        for wire in flat_wires.root2wires.keys() {
            site_wire_to_pins.insert(wire, Vec::new());
        }
        for (i, bel_pin) in site_bel_pins.iter().enumerate() {
            site_wire_to_pins.entry(&bel_pin.site_wire).or_insert(Vec::new()).push(i)
        }

        let mut site_wires = Vec::new();
        for (wire, pins) in site_wire_to_pins.iter() {
            site_wires.push(SiteWire {
                name: wire.to_string(),
                bel_pins: pins.clone(),
            })
        }

        let mut shuffled_pins = Vec::new();
        shuffled_pins.extend(pins.iter().filter(|p| p.dir == PinDir::INPUT).cloned());
        let last_input_pin =  shuffled_pins.len() - 1;
        shuffled_pins.extend(pins.iter().filter(|p| p.dir != PinDir::INPUT).cloned());
        sites.push(Site {
            name: "PLC".to_string(),
            site_type: "PLC".to_string(),
            pins: shuffled_pins,
            last_input_pin: last_input_pin,
            wires: site_wires,
            bel_pins: site_bel_pins,
            bels: site_bels,
            pips: site_pips,
        });
    }
    return sites;
}
<commit_msg>Fix LUT output wire<commit_after>use crate::database::*;
use crate::wires::*;
use crate::bels::*;

use std::collections::{BTreeSet, BTreeMap};
use std::fmt;

#[derive(Clone)]
pub struct SitePin {
    pub tile_wire: String,
    pub site_wire: String,
    pub dir: PinDir,
    pub bel_pin: usize,
}

impl fmt::Debug for SitePin {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {} {}",
            &self.tile_wire,
            match self.dir {
                PinDir::INPUT => "-->",
                PinDir::OUTPUT => "<--",
                PinDir::INOUT => "<->",
            },
            &self.site_wire
        )
    }
}

#[derive(Clone)]
pub struct SiteRoutingBel {
    pub src_wires: Vec<String>,
    pub dst_wire: String,
}

impl fmt::Debug for SiteRoutingBel {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} <-- {{", &self.dst_wire)?;
        let mut first = true;
        for src in self.src_wires.iter() {
            if !first { write!(f, ", ")?; }
            write!(f, "{}", src)?;
            first = false;
        }
        write!(f, "}}")?;
        Ok(())
    }
}

#[derive(Clone)]
pub struct SiteBelPin {
    pub bel_name: String,
    pub pin_name: String,
    pub site_wire: String,
    pub dir: PinDir,
}

impl fmt::Debug for SiteBelPin {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        write!(f, "{} {} {}",
            &self.site_wire,
            match self.dir {
                PinDir::INPUT => "-->",
                PinDir::OUTPUT => "<--",
                PinDir::INOUT => "<->",
            },
            &self.pin_name
        )
    }
}

#[derive(Clone, Eq, PartialEq)]
pub enum SiteBelClass {
    BEL,
    RBEL,
    PORT,
}

#[derive(Clone)]
pub struct SiteBel {
    pub name: String,
    pub bel_type: String,
    pub bel_class: SiteBelClass,
    pub pins: Vec<usize>,
}

#[derive(Clone)]
pub struct SiteWire {
    pub name: String,
    pub bel_pins: Vec<usize>,
}

#[derive(Clone)]
pub struct SitePIP {
    pub in_pin: usize,
    pub out_pin: usize,
}

#[derive(Clone)]
pub struct Site {
    pub name: String,
    pub site_type: String,
    pub wires: Vec<SiteWire>,
    pub bel_pins: Vec<SiteBelPin>,
    pub pins: Vec<SitePin>,
    pub last_input_pin: usize,
    pub bels: Vec<SiteBel>,
    pub pips: Vec<SitePIP>,
}

// To speed up site routing; where fixed connections connect multiple site wires together, merge them into one
// this structure stores the mapping in both directions
struct SiteWireMap {
    pub wire2root: BTreeMap<String, String>,
    pub root2wires: BTreeMap<String, BTreeSet<String>>,
}

impl SiteWireMap {
    pub fn lookup_wire(&self, name: &str) -> String {
        self.wire2root.get(name).unwrap_or(&name.to_string()).to_string()
    }
    pub fn add_alias(&mut self, root: &str, wire: &str) {
        self.wire2root.entry(wire.to_string()).or_insert(root.to_string());
        self.root2wires.entry(root.to_string()).or_insert(BTreeSet::new()).insert(wire.to_string());
        if let Some(wires) = self.root2wires.get(wire).cloned() {
            for w in wires {
                *self.wire2root.get_mut(&w).unwrap() = root.to_string();
                self.root2wires.get_mut(root).unwrap().insert(w.to_string());
            }
            self.root2wires.remove(wire);
        }
    }
}

// determine which wire has priority when flattening connections
fn compare_wire(a: &str, b: &str) -> bool {
    // prioritise shorter wires first, then fall back to a lexicographical compare
    (a.len(), a) < (b.len(), b)
}

fn flatten_wires(tiletype: &str, tiledata: &TileBitsDatabase) -> SiteWireMap {
    let mut m = SiteWireMap {
        wire2root: BTreeMap::new(),
        root2wires: BTreeMap::new(),
    };
    for (dst_wire, conns) in tiledata.conns.iter() {
        if !is_site_wire(tiletype, dst_wire) {
            continue;
        }
        for c in conns {
            let looked_up_src = m.lookup_wire(&c.from_wire);
            let looked_up_dst = m.lookup_wire(dst_wire);
            if compare_wire(&looked_up_src, &looked_up_dst) && is_site_wire(tiletype, &c.from_wire) {
                m.add_alias(&looked_up_src, &looked_up_dst);
            } else {
                m.add_alias(&looked_up_dst, &looked_up_src);
            }
        }
    }
    return m;
}

pub fn build_sites(tiletype: &str, tiledata: &TileBitsDatabase) -> Vec<Site> {
    // TODO: handle other tile types
    let mut sites = Vec::new();
    if tiletype == "PLC" {
        let flat_wires = flatten_wires(tiletype, tiledata);
        let mut pins = Vec::new();
        let mut found_pins = BTreeSet::new();
        for (dst_wire, conns) in tiledata.conns.iter() {
            for conn in conns.iter() {
                if is_site_wire(tiletype, dst_wire) && !is_site_wire(tiletype, &conn.from_wire) {
                    // from tile into site
                    let site_dst_wire = flat_wires.lookup_wire(dst_wire);
                    if found_pins.contains(&(conn.from_wire.to_string(), site_dst_wire.to_string())) {
                        continue;
                    }
                    found_pins.insert((conn.from_wire.to_string(), site_dst_wire.to_string()));
                    pins.push(SitePin {
                        tile_wire: conn.from_wire.clone(),
                        site_wire: site_dst_wire,
                        dir: PinDir::INPUT,
                        bel_pin: 0,
                    });
                } else if !is_site_wire(tiletype, dst_wire) && is_site_wire(tiletype, &conn.from_wire) {
                    // from site into tile
                    let site_src_wire = flat_wires.lookup_wire(&conn.from_wire);
                    if found_pins.contains(&(site_src_wire.to_string(), dst_wire.to_string())) {
                        continue;
                    }
                    found_pins.insert((site_src_wire.to_string(), dst_wire.to_string()));
                    pins.push(SitePin {
                        tile_wire: dst_wire.to_string(),
                        site_wire: site_src_wire,
                        dir: PinDir::OUTPUT,
                        bel_pin: 0,
                    });
                }
            }
        }
        // For the F outputs where have a MUX/LUT choice; use the FF DI mux because it's not independently controllable from the FF mux
        for i in 0..4 {
                pins.push(SitePin {
                    tile_wire: format!("JF{}", 2*i),
                    site_wire: flat_wires.lookup_wire(&format!("JDI0_SLICE{}", &"ABCD"[i..i+1])),
                    dir: PinDir::OUTPUT,
                    bel_pin: 0,
                });
        }
        let mut site_bel_pins = Vec::new();
        let mut site_bels = Vec::new();
        let mut site_pips = Vec::new();
        // Import functional bels
        let orig_bels = get_tile_bels(&tiletype, tiledata);
        for orig_bel in orig_bels.iter() {
            let mut bel_pins = Vec::new();

            for pin in &orig_bel.pins {
                // TODO: relative X and Y coordinates
                let wire_name = pin.wire.rel_name(orig_bel.rel_x, orig_bel.rel_y);
                assert!(is_site_wire(tiletype, &wire_name));
                let site_wire = flat_wires.lookup_wire(&wire_name);
                bel_pins.push(site_bel_pins.len());
                site_bel_pins.push(SiteBelPin {
                    bel_name: orig_bel.name.clone(),
                    pin_name: pin.name.clone(),
                    site_wire: site_wire,
                    dir: pin.dir.clone(),
                });
            }

            site_bels.push(SiteBel {
                name: orig_bel.name.clone(),
                bel_class: SiteBelClass::BEL,
                bel_type: orig_bel.beltype.clone(),
                pins: bel_pins,
            });
        }
        // Convert pips to routing bels
        for (dst_wire, pips) in tiledata.pips.iter() {
            if !is_site_wire(tiletype, dst_wire) {
                continue;
            }
            let mut bel_pins = Vec::new();
            let site_dst_wire = flat_wires.lookup_wire(dst_wire);
            let bel_name = format!("RBEL_{}", site_dst_wire);
            let out_pin_idx = site_bel_pins.len();
            bel_pins.push(out_pin_idx);
            site_bel_pins.push(SiteBelPin {
                bel_name: bel_name.clone(),
                pin_name: site_dst_wire.clone(),
                site_wire: site_dst_wire.clone(),
                dir: PinDir::OUTPUT,
            });
            for src_wire in pips.iter().map(|p| &p.from_wire).filter(|w| is_site_wire(tiletype, w)).map(|w| flat_wires.lookup_wire(w)) {
                site_pips.push(SitePIP {
                    in_pin: site_bel_pins.len(),
                    out_pin: out_pin_idx,
                });
                bel_pins.push(site_bel_pins.len());
                site_bel_pins.push(SiteBelPin {
                    bel_name: bel_name.clone(),
                    pin_name: src_wire.clone(),
                    site_wire: src_wire.clone(),
                    dir: PinDir::INPUT,
                });
            }

            site_bels.push(SiteBel {
                name: bel_name.clone(),
                bel_class: SiteBelClass::RBEL,
                bel_type: bel_name.clone(),
                pins: bel_pins,
            });
        }
        // Create port bels
        for pin in pins.iter_mut() {
            let bel_name = pin.site_wire.clone();
            let bel_pins = vec![site_bel_pins.len()];
            pin.bel_pin = site_bel_pins.len();
            site_bel_pins.push(SiteBelPin {
                bel_name: bel_name.clone(),
                pin_name: pin.site_wire.clone(),
                site_wire: pin.site_wire.clone(),
                dir: match pin.dir {
                    PinDir::INPUT => PinDir::OUTPUT,
                    PinDir::OUTPUT => PinDir::INPUT,
                    PinDir::INOUT => PinDir::INOUT,
                }
            });
            site_bels.push(SiteBel {
                name: bel_name.clone(),
                bel_class: SiteBelClass::PORT,
                bel_type: bel_name.clone(),
                pins: bel_pins,
            })
        }

        let mut site_wire_to_pins = BTreeMap::new();
        for wire in flat_wires.root2wires.keys() {
            site_wire_to_pins.insert(wire, Vec::new());
        }
        for (i, bel_pin) in site_bel_pins.iter().enumerate() {
            site_wire_to_pins.entry(&bel_pin.site_wire).or_insert(Vec::new()).push(i)
        }

        let mut site_wires = Vec::new();
        for (wire, pins) in site_wire_to_pins.iter() {
            site_wires.push(SiteWire {
                name: wire.to_string(),
                bel_pins: pins.clone(),
            })
        }

        let mut shuffled_pins = Vec::new();
        shuffled_pins.extend(pins.iter().filter(|p| p.dir == PinDir::INPUT).cloned());
        let last_input_pin =  shuffled_pins.len() - 1;
        shuffled_pins.extend(pins.iter().filter(|p| p.dir != PinDir::INPUT).cloned());
        sites.push(Site {
            name: "PLC".to_string(),
            site_type: "PLC".to_string(),
            pins: shuffled_pins,
            last_input_pin: last_input_pin,
            wires: site_wires,
            bel_pins: site_bel_pins,
            bels: site_bels,
            pips: site_pips,
        });
    }
    return sites;
}
<|endoftext|>


<commit_before>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::HashMap;
use std::slice::IterMut;

use uuid::Uuid;

use super::engine::{HasName, HasUuid};

/// Map UUID and name to T items.
#[derive(Debug)]
pub struct Table<T: HasName + HasUuid> {
    items: Vec<T>,
    name_map: HashMap<String, usize>,
    uuid_map: HashMap<Uuid, usize>,
}

/// All operations are O(1).
/// The implementation does not priviledge the name key over the UUID key
/// in any way. They are both treated as constants once the item has been
/// inserted. In order to rename a T item, it must be removed, renamed, and
/// reinserted under the new name.
impl<T: HasName + HasUuid> Table<T> {
    pub fn new() -> Self {
        Table {
            items: Vec::new(),
            name_map: HashMap::new(),
            uuid_map: HashMap::new(),
        }

    }

    pub fn is_empty(&self) -> bool {
        self.items.is_empty()
    }

    pub fn len(&self) -> usize {
        self.items.len()
    }

    /// Returns true if map has an item corresponding to this name, else false.
    pub fn contains_name(&self, name: &str) -> bool {
        self.name_map.contains_key(name)
    }

    /// Returns true if map has an item corresponding to this uuid, else false.
    #[allow(dead_code)]
    pub fn contains_uuid(&self, uuid: &Uuid) -> bool {
        self.uuid_map.contains_key(uuid)
    }

    /// Get item by name.
    pub fn get_by_name(&self, name: &str) -> Option<&T> {
        self.name_map.get(name).map(|index| &self.items[*index])
    }

    /// Get item by uuid.
    pub fn get_by_uuid(&self, uuid: &Uuid) -> Option<&T> {
        self.uuid_map.get(uuid).map(|index| &self.items[*index])
    }

    /// Get mutable item by name.
    pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut T> {
        if let Some(index) = self.name_map.get(name) {
            Some(&mut self.items[*index])
        } else {
            None
        }
    }

    /// Get mutable item by uuid.
    pub fn get_mut_by_uuid(&mut self, uuid: &Uuid) -> Option<&mut T> {
        if let Some(index) = self.uuid_map.get(uuid) {
            Some(&mut self.items[*index])
        } else {
            None
        }
    }

    /// A mutable iterator through Pools.
    #[allow(dead_code)]
    pub fn iter_mut(&mut self) -> IterMut<T> {
        self.items.iter_mut()
    }

    /// Removes the Pool corresponding to name if there is one.
    pub fn remove_by_name(&mut self, name: &str) -> Option<T> {
        if let Some(index) = self.name_map.remove(name) {
            // There is guaranteed to be a last because there is at least
            // one index into the items.

            // Insert mappings for the about-to-be swapped element
            {
                let last_item = &self.items.last().unwrap();
                self.name_map.insert(last_item.name().into(), index);
                self.uuid_map.insert(last_item.uuid().clone(), index);
            }

            // Remove the item we want to remove and also the uuid mapping
            let item = self.items.swap_remove(index);
            self.uuid_map.remove(item.uuid());

            // Remove the name again, in case there is only one item.
            self.name_map.remove(name);

            Some(item)
        } else {
            None
        }
    }

    /// Removes the Pool corresponding to the uuid if there is one.
    pub fn remove_by_uuid(&mut self, uuid: &Uuid) -> Option<T> {
        if let Some(index) = self.uuid_map.remove(uuid) {
            // There is guaranteed to be a last because there is at least
            // one index into the items.

            // Insert mappings for the about-to-be swapped element
            {
                let last_item = &self.items.last().unwrap();
                self.name_map.insert(last_item.name().into(), index);
                self.uuid_map.insert(last_item.uuid().clone(), index);
            }

            // Remove the item we want to remove and also the uuid mapping
            let item = self.items.swap_remove(index);
            self.name_map.remove(item.name());

            // Remove the uuid again, in case there is only one item.
            self.uuid_map.remove(uuid);

            Some(item)
        } else {
            None
        }
    }

    /// Inserts an item for given uuid and name.
    /// Returns a list of the items displaced, which may be empty if no items
    /// are displaced, have one entry if the uuid and the name map to the same
    /// item, and may have two entries if the uuid and the name map to
    /// different items.
    pub fn insert(&mut self, item: T) -> Vec<T> {
        let name_item = self.remove_by_name(item.name());
        let uuid_item = self.remove_by_uuid(item.uuid());

        let future_last_index = self.items.len();
        self.name_map.insert(item.name().into(), future_last_index);
        self.uuid_map.insert(item.uuid().clone(), future_last_index);

        self.items.push(item);

        match (name_item, uuid_item) {
            (None, None) => vec![],
            (None, Some(item)) => vec![item],
            (Some(item), None) => vec![item],
            (Some(p1), Some(p2)) => vec![p1, p2],
        }
    }
}
<commit_msg>Add some simple tests to the table data structure<commit_after>// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

use std::collections::HashMap;
use std::slice::IterMut;

use uuid::Uuid;

use super::engine::{HasName, HasUuid};

/// Map UUID and name to T items.
#[derive(Debug)]
pub struct Table<T: HasName + HasUuid> {
    items: Vec<T>,
    name_map: HashMap<String, usize>,
    uuid_map: HashMap<Uuid, usize>,
}

/// All operations are O(1).
/// The implementation does not priviledge the name key over the UUID key
/// in any way. They are both treated as constants once the item has been
/// inserted. In order to rename a T item, it must be removed, renamed, and
/// reinserted under the new name.
impl<T: HasName + HasUuid> Table<T> {
    pub fn new() -> Self {
        Table {
            items: Vec::new(),
            name_map: HashMap::new(),
            uuid_map: HashMap::new(),
        }

    }

    pub fn is_empty(&self) -> bool {
        self.items.is_empty()
    }

    pub fn len(&self) -> usize {
        self.items.len()
    }

    /// Returns true if map has an item corresponding to this name, else false.
    pub fn contains_name(&self, name: &str) -> bool {
        self.name_map.contains_key(name)
    }

    /// Returns true if map has an item corresponding to this uuid, else false.
    #[allow(dead_code)]
    pub fn contains_uuid(&self, uuid: &Uuid) -> bool {
        self.uuid_map.contains_key(uuid)
    }

    /// Get item by name.
    pub fn get_by_name(&self, name: &str) -> Option<&T> {
        self.name_map.get(name).map(|index| &self.items[*index])
    }

    /// Get item by uuid.
    pub fn get_by_uuid(&self, uuid: &Uuid) -> Option<&T> {
        self.uuid_map.get(uuid).map(|index| &self.items[*index])
    }

    /// Get mutable item by name.
    pub fn get_mut_by_name(&mut self, name: &str) -> Option<&mut T> {
        if let Some(index) = self.name_map.get(name) {
            Some(&mut self.items[*index])
        } else {
            None
        }
    }

    /// Get mutable item by uuid.
    pub fn get_mut_by_uuid(&mut self, uuid: &Uuid) -> Option<&mut T> {
        if let Some(index) = self.uuid_map.get(uuid) {
            Some(&mut self.items[*index])
        } else {
            None
        }
    }

    /// A mutable iterator through Pools.
    #[allow(dead_code)]
    pub fn iter_mut(&mut self) -> IterMut<T> {
        self.items.iter_mut()
    }

    /// Removes the Pool corresponding to name if there is one.
    pub fn remove_by_name(&mut self, name: &str) -> Option<T> {
        if let Some(index) = self.name_map.remove(name) {
            // There is guaranteed to be a last because there is at least
            // one index into the items.

            // Insert mappings for the about-to-be swapped element
            {
                let last_item = &self.items.last().unwrap();
                self.name_map.insert(last_item.name().into(), index);
                self.uuid_map.insert(last_item.uuid().clone(), index);
            }

            // Remove the item we want to remove and also the uuid mapping
            let item = self.items.swap_remove(index);
            self.uuid_map.remove(item.uuid());

            // Remove the name again, in case there is only one item.
            self.name_map.remove(name);

            Some(item)
        } else {
            None
        }
    }

    /// Removes the Pool corresponding to the uuid if there is one.
    pub fn remove_by_uuid(&mut self, uuid: &Uuid) -> Option<T> {
        if let Some(index) = self.uuid_map.remove(uuid) {
            // There is guaranteed to be a last because there is at least
            // one index into the items.

            // Insert mappings for the about-to-be swapped element
            {
                let last_item = &self.items.last().unwrap();
                self.name_map.insert(last_item.name().into(), index);
                self.uuid_map.insert(last_item.uuid().clone(), index);
            }

            // Remove the item we want to remove and also the uuid mapping
            let item = self.items.swap_remove(index);
            self.name_map.remove(item.name());

            // Remove the uuid again, in case there is only one item.
            self.uuid_map.remove(uuid);

            Some(item)
        } else {
            None
        }
    }

    /// Inserts an item for given uuid and name.
    /// Returns a list of the items displaced, which may be empty if no items
    /// are displaced, have one entry if the uuid and the name map to the same
    /// item, and may have two entries if the uuid and the name map to
    /// different items.
    pub fn insert(&mut self, item: T) -> Vec<T> {
        let name_item = self.remove_by_name(item.name());
        let uuid_item = self.remove_by_uuid(item.uuid());

        let future_last_index = self.items.len();
        self.name_map.insert(item.name().into(), future_last_index);
        self.uuid_map.insert(item.uuid().clone(), future_last_index);

        self.items.push(item);

        match (name_item, uuid_item) {
            (None, None) => vec![],
            (None, Some(item)) => vec![item],
            (Some(item), None) => vec![item],
            (Some(p1), Some(p2)) => vec![p1, p2],
        }
    }
}

#[cfg(test)]
mod tests {

    use rand;
    use uuid::Uuid;

    use super::super::engine::{HasName, HasUuid};

    use super::Table;

    #[derive(Debug)]
    struct TestThing {
        name: String,
        uuid: Uuid,
        stuff: u32,
    }

    impl TestThing {
        pub fn new(name: &str, uuid: &Uuid) -> TestThing {
            TestThing {
                name: name.to_owned(),
                uuid: uuid.clone(),
                stuff: rand::random::<u32>(),
            }
        }
    }

    impl HasUuid for TestThing {
        fn uuid(&self) -> &Uuid {
            &self.uuid
        }
    }

    impl HasName for TestThing {
        fn name(&self) -> &str {
            &self.name
        }
    }

    #[test]
    /// Remove a test object by its uuid.
    /// Mutate the removed test object.
    /// Verify that the table is now empty and that removing by name yields
    /// no result.
    fn remove_existing_item() {
        let mut t: Table<TestThing> = Table::new();
        let uuid = Uuid::new_v4();
        let name = "name";
        t.insert(TestThing::new(&name, &uuid));
        assert!(t.get_by_name(&name).is_some());
        assert!(t.get_by_uuid(&uuid).is_some());
        let thing = t.remove_by_uuid(&uuid);
        assert!(thing.is_some());
        let mut thing = thing.unwrap();
        thing.stuff = 0;
        assert!(t.is_empty());
        assert!(t.remove_by_name(&name).is_none());
        assert!(t.get_by_name(&name).is_none());
        assert!(t.get_by_uuid(&uuid).is_none());
    }

    #[test]
    /// Insert a thing and then insert another thing with same keys.
    /// The previously inserted thing should be returned.
    /// You can't insert the identical thing, because that would be a move.
    /// This is good, because then you can't have a thing that is both in
    /// the table and not in the table.
    fn insert_same_keys() {
        let mut t: Table<TestThing> = Table::new();
        let uuid = Uuid::new_v4();
        let name = "name";
        let thing = TestThing::new(&name, &uuid);
        let thing_key = thing.stuff;
        let displaced = t.insert(thing);

        // There was nothing previously, so displaced must be empty.
        assert!(displaced.is_empty());

        // t now contains the inserted thing.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(&uuid));
        assert!(t.get_by_uuid(&uuid).unwrap().stuff == thing_key);

        // Add another thing with the same keys.
        let thing2 = TestThing::new(&name, &uuid);
        let thing_key2 = thing2.stuff;
        let displaced = t.insert(thing2);

        // It has displaced the old thing.
        assert!(displaced.len() == 1);
        let ref displaced_item = displaced[0];
        assert!(displaced_item.name() == name);
        assert!(displaced_item.uuid() == &uuid);

        // But it contains a thing with the same keys.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(&uuid));
        assert!(t.get_by_uuid(&uuid).unwrap().stuff == thing_key2);
        assert!(t.len() == 1);
    }

    #[test]
    /// Insert a thing and then insert another thing with the same name.
    /// The previously inserted thing should be returned.
    fn insert_same_name() {
        let mut t: Table<TestThing> = Table::new();
        let uuid = Uuid::new_v4();
        let name = "name";
        let thing = TestThing::new(&name, &uuid);
        let thing_key = thing.stuff;

        // There was nothing in the table before, so displaced is empty.
        let displaced = t.insert(thing);
        assert!(displaced.is_empty());

        // t now contains thing.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(&uuid));

        // Insert new item with different UUID.
        let uuid2 = Uuid::new_v4();
        let thing2 = TestThing::new(&name, &uuid2);
        let thing_key2 = thing2.stuff;
        let displaced = t.insert(thing2);

        // The items displaced consist exactly of the first item.
        assert!(displaced.len() == 1);
        let ref displaced_item = displaced[0];
        assert!(displaced_item.name() == name);
        assert!(displaced_item.uuid() == &uuid);
        assert!(displaced_item.stuff == thing_key);

        // The table contains the new item and has no memory of the old.
        assert!(t.contains_name(&name));
        assert!(t.contains_uuid(&uuid2));
        assert!(!t.contains_uuid(&uuid));
        assert!(t.get_by_uuid(&uuid2).unwrap().stuff == thing_key2);
        assert!(t.get_by_name(&name).unwrap().stuff == thing_key2);
        assert!(t.len() == 1);
    }
}
<|endoftext|>


<commit_before>use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::fs::{File, Metadata, metadata};
use std::io::{self, Read};
use std::path::{PathBuf, Path, Component};
use std::rc::Rc;
use std::sync::Arc;

use quire::{self, Pos, Include, ErrorCollector, Options, parse_config};
use quire::{raw_parse as parse_yaml};
use quire::ast::{Ast, process as process_ast};
use regex;

use config::authorizers::Authorizer;
use config::root::{ConfigData, ConfigSource, Mixin};
use config::root::{config_validator, mixin_validator};
use super::Handler;
use config::static_files::Mode;
use config::log;
use intern::{LogFormatName, Authorizer as AuthorizerName, HandlerName};
use routing::RoutingTable;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Io(err: io::Error) {
            display("IO error: {}", err)
            description("IO error")
            from()
        }
        Config(err: quire::ErrorList) {
            display("config error: {}", err)
            description("config error")
            from()
        }
        Validation(err: String) {
            display("validation error: {}", err)
            description("validation error")
            from()
        }
        Routing(err: String) {
            display("routing table error: {}", err)
            description("routing table error")
        }
        BadMixinPath(filename: PathBuf) {
            display("bad mixin path: {:?}, \
                only relative paths are allowed", filename)
            description("bad mixin path")
        }
        InvalidPrefixInMixin(prefix: String, filename: PathBuf,
                             typ: &'static str, name: String)
        {
            display("mixin {:?} has {} named {:?} without prefix {:?}",
                filename, typ, name, prefix)
            description("item in mixin file has invalid prefix")
        }
        MixinConflict(filename: PathBuf, typ: &'static str, name: String) {
            display("mixin {:?} has overlapping {} {:?}",
                filename, typ, name)
            description("mixin has overlapping item")
        }
        Regex(err: regex::Error) {
            display("error compiling routing regex: {}", err)
            description("error compiling routing regex")
            from()
        }
        NoHandler(name: HandlerName) {
            display("handler {:?} not found", name)
            description("handler not found")
        }
        NoAuthorizer(name: AuthorizerName) {
            display("authorizer {:?} not found", name)
            description("authorizer not found")
        }
    }
}

macro_rules! err {
    // Shortcut to config post-load validation error
    ($msg:expr, $($a:expr),*) => (
        return Err(format!($msg, $($a),*).into())
    )
}

fn join_filename(base: &Path, relative: &Path) -> Result<PathBuf, ()> {
    let mut path = PathBuf::from(&*base);
    path.pop(); // pop original filename
    for component in relative.components() {
        match component {
            Component::Normal(x) => path.push(x),
            _ => {
                return Err(());
            }
        }
    }
    Ok(path)
}

#[allow(dead_code)]
pub fn include_file(files: &RefCell<&mut Vec<(PathBuf, String, Metadata)>>,
    pos: &Pos, include: &Include,
    err: &ErrorCollector, options: &Options)
    -> Ast
{
    match *include {
        Include::File { filename } => {
            let path = match join_filename(&Path::new(&**pos.filename),
                                           &Path::new(filename))
            {
                Ok(path) => path,
                Err(()) => {
                    // TODO(tailhook) should this error exist?
                    err.add_error(quire::Error::preprocess_error(pos,
                        format!("Only relative paths without parent \
                                 directories can be included")));
                    return Ast::void(pos);
                }
            };

            debug!("{} Including {:?}", pos, path);

            let mut body = String::new();
            File::open(&path)
            .and_then(|mut f| {
                let m = f.metadata();
                f.read_to_string(&mut body)?;
                m
            })
            .map_err(|e| {
                err.add_error(quire::Error::open_error(&path, e))
            }).ok()
            .and_then(|metadata| {
                files.borrow_mut().push((
                    path.to_path_buf(),
                    String::from(filename),
                    metadata,
                ));
                parse_yaml(Rc::new(path.display().to_string()), &body,
                    |doc| { process_ast(&options, doc, err) },
                ).map_err(|e| err.add_error(e)).ok()
            })
            .unwrap_or_else(|| Ast::void(pos))
        }
    }
}

fn prefix_error<N: fmt::Display>(prefix: &str, filename: &Path,
    typ: &'static str, name: &N)
    -> Error
{
    Error::InvalidPrefixInMixin(prefix.to_string(), filename.to_path_buf(),
        typ, name.to_string())
}

fn conflict<N: fmt::Display>(filename: &Path, typ: &'static str, name: N)
    -> Error
{
    Error::MixinConflict(filename.to_path_buf(), typ, name.to_string())
}

fn mix_in<K, V>(
    filename: &Path, prefix: &str,
    dest: &mut HashMap<K, V>, src: HashMap<K, V>,
    typ: &'static str)
    -> Result<(), Error>
    where K: ::std::hash::Hash + ::std::ops::Deref<Target=str>,
          K: ::std::fmt::Display + Eq,
{
    for (h, handler) in src {
        if !(&*h).starts_with(prefix) {
            return Err(prefix_error(prefix, filename, typ, &h));
        }
        if dest.contains_key(&h) {
            return Err(conflict(filename, typ, &h));
        }
        dest.insert(h, handler);
    }
    Ok(())
}

pub fn read_config<P: AsRef<Path>>(filename: P)
    -> Result<(ConfigData, Vec<(PathBuf, String, Metadata)>), Error>
{
    let filename = filename.as_ref();
    let mut files = Vec::new();
    files.push((
        filename.to_path_buf(),
        String::from("<main>"),
        metadata(filename)?,
    ));
    let mut src: ConfigSource = {
        let cell = RefCell::new(&mut files);
        let mut opt = Options::default();
        opt.allow_include(
            |a, b, c, d| include_file(&cell, a, b, c, d));
        parse_config(filename, &config_validator(), &opt)?
    };

    for (prefix, ref incl) in &src.mixins {
        let incl_path = join_filename(filename, &Path::new(incl))
            .map_err(|()| Error::BadMixinPath(filename.to_path_buf()))?;
        files.push((
            incl_path.clone(),
            incl.display().to_string(),
            metadata(&incl_path)?,
        ));

        let mixin: Mixin = {
            let cell = RefCell::new(&mut files);
            let mut opt = Options::default();
            opt.allow_include(
                |a, b, c, d| include_file(&cell, a, b, c, d));
            parse_config(&incl_path, &mixin_validator(), &opt)?
        };
        mix_in(&incl_path, prefix,
               &mut src.handlers, mixin.handlers, "handler")?;
        mix_in(&incl_path, prefix,
               &mut src.authorizers, mixin.authorizers, "authorizer")?;
        mix_in(&incl_path, prefix,
               &mut src.session_pools, mixin.session_pools, "session-pool")?;
        mix_in(&incl_path, prefix, &mut src.http_destinations,
            mixin.http_destinations, "http-destination")?;
        mix_in(&incl_path, prefix, &mut src.ldap_destinations,
            mixin.ldap_destinations, "ldap-destination")?;
        mix_in(&incl_path, prefix,
            &mut src.networks, mixin.networks, "network")?;
        mix_in(&incl_path, prefix,
            &mut src.log_formats, mixin.log_formats, "log-format")?;
        mix_in(&incl_path, prefix,
            &mut src.disk_pools, mixin.disk_pools, "disk-pools")?;
    }
    return Ok((postprocess_config(src)?, files));
}

pub fn postprocess_config(mut src: ConfigSource)
    -> Result<ConfigData, Error>
{
    // Set some defaults
    if !src.log_formats.contains_key("debug-log") {
        src.log_formats.insert(LogFormatName::from("debug-log"),
            log::Format::from_string(r#"
                {{ request.client_ip }}
                {{ request.host }}
                "{{ request.method }}
                {{ request.path }}
                {{ request.version }}"
                {{ response.status_code }}
            "#.into()).expect("can always compile debug log"));
    }

    if !src.authorizers.contains_key("default") {
        src.authorizers.insert(AuthorizerName::from("default"),
                               Authorizer::AllowAll);
    }

    let mut cfg = ConfigData {
        routing: RoutingTable::new(&src)?,

        listen: src.listen,
        max_connections: src.max_connections,
        pipeline_depth: src.pipeline_depth,
        listen_error_timeout: src.listen_error_timeout,
        first_byte_timeout: src.first_byte_timeout,
        keep_alive_timeout: src.keep_alive_timeout,
        headers_timeout: src.headers_timeout,
        input_body_byte_timeout: src.input_body_byte_timeout,
        input_body_whole_timeout: src.input_body_whole_timeout,
        output_body_byte_timeout: src.output_body_byte_timeout,
        output_body_whole_timeout: src.output_body_whole_timeout,

        handlers: src.handlers,
        authorizers: src.authorizers,
        session_pools: src.session_pools,
        http_destinations: src.http_destinations,
        ldap_destinations: src.ldap_destinations,
        networks: src.networks,
        log_formats: src.log_formats,
        disk_pools: src.disk_pools,

        replication: src.replication,
        debug_routing: src.debug_routing,
        debug_logging: src.debug_logging,
        server_name: src.server_name,

        set_user: src.set_user,
        set_group: src.set_group,
    };

    // Extra config validations

    /* TODO
    for &(ref domain, ref sub) in cfg.routing.hosts() {
        for (path, route) in sub {
            if cfg.handlers.get(&route.destination).is_none() {
                err!("Unknown handler {:?}", route.destination)
            }
            if path.as_ref().map(|x| x.ends_with("/")).unwrap_or(false) {
                err!("Path must not end with /: {:?} {:?}",
                     domain, path);
            }
            if let Some(&Handler::StripWWWRedirect) =
                cfg.handlers.get(&route.destination)
            {
                if !domain.matches_www() {
                    err!(concat!("Expected `www.` prefix for StripWWWRedirect",
                                 " handler route: {:?} {:?}"), domain, path);
                }
            }
        }
    }
    */

    for (name, h) in &cfg.handlers {
        match h {
            &Handler::SwindonLattice(ref chat) => {
                match cfg.session_pools.get_mut(&chat.session_pool) {
                    None => {
                        err!("No session pool {:?} defined", chat.session_pool)
                    }
                    Some(mut pool) => {
                        let tangle = chat.use_tangle_prefix();
                        if pool.use_tangle_prefix.is_some() &&
                            pool.use_tangle_prefix != Some(tangle)
                        {
                            err!("Inconsistent `compatibility` for \
                                  pool {:?}", chat.session_pool);
                        }
                        Arc::make_mut(&mut pool)
                            .use_tangle_prefix = Some(tangle);

                        let dest = if tangle {
                            chat.message_handlers.resolve(
                                "tangle.session_inactive")
                        } else {
                            chat.message_handlers.resolve(
                                "swindon.session_inactive")
                        };

                        let tangle = chat.use_tangle_auth();
                        if pool.use_tangle_auth.is_some() &&
                            pool.use_tangle_auth != Some(tangle)
                        {
                            err!("Inconsistent `compatibility` for \
                                  pool {:?}", chat.session_pool);
                        }
                        Arc::make_mut(&mut pool)
                            .use_tangle_auth = Some(tangle);

                        let tangle = chat.weak_content_type();
                        if pool.weak_content_type.is_some() &&
                            pool.weak_content_type != Some(tangle)
                        {
                            err!("Inconsistent `compatibility` for \
                                  pool {:?}", chat.session_pool);
                        }
                        Arc::make_mut(&mut pool)
                            .weak_content_type = Some(tangle);

                        if !pool.inactivity_handlers.contains(dest) &&
                            pool.inactivity_handlers.len() != 0 {
                            err!(concat!(
                                "Inactivity destinations mismatch for",
                                 "{:?}: {:?}"), name, dest)
                        }
                    }
                }
                if let Some(h) = chat.http_route.as_ref() {
                    if !cfg.handlers.contains_key(h) {
                        err!("{:?}: unknown http route {:?}", name, h)
                    }
                }
                let u = &chat.message_handlers.default.upstream;
                if let Some(http_dest) = cfg.http_destinations.get(u) {
                    if http_dest.override_host_header.is_none() {
                        err!("http destination {:?} is used \
                             in message-handler of {:?}, so must contain \
                             override-host-header setting.", u, name);
                    }
                } else {
                    err!("{:?}: unknown http destination {:?}", name, u)
                }
                for (_, dest) in &chat.message_handlers.map {
                    if !cfg.http_destinations.contains_key(&dest.upstream) {
                        err!("{:?}: unknown http destination {:?}",
                             name, dest.upstream)
                    }
                }
            }
            &Handler::Proxy(ref proxy) => {
                let u = &proxy.destination.upstream;
                if !cfg.http_destinations.contains_key(u) {
                    err!("{:?}: unknown http destination {:?}", name, u)
                }
                if proxy.request_id_header.is_some() {
                    warn!(concat!(
                        "{:?}: request_id_header is deprecated",
                        " in !Proxy setting, it must be specified",
                        " in http destination"), name);
                }
            }
            &Handler::Static(ref config) => {
                if config.strip_host_suffix.is_some() &&
                   config.mode != Mode::with_hostname
                {
                    err!("{:?}: `strip-host-suffix` only \
                        works when `mode: with-hostname`", name);
                }
            }
            _ => {}
        }
    }
    // TODO: verify session_pool inactivity handlers
    for (name, s) in &cfg.session_pools {
        for dest in &s.inactivity_handlers {
            if !cfg.http_destinations.contains_key(&dest.upstream) {
                err!("{:?}: unknown http destination {:?}",
                     name, dest.upstream)
            }
        }
    }

    Ok(cfg)
}
<commit_msg>Add implicit "default" handler<commit_after>use std::cell::RefCell;
use std::collections::HashMap;
use std::fmt;
use std::fs::{File, Metadata, metadata};
use std::io::{self, Read};
use std::path::{PathBuf, Path, Component};
use std::rc::Rc;
use std::sync::Arc;

use quire::{self, Pos, Include, ErrorCollector, Options, parse_config};
use quire::{raw_parse as parse_yaml};
use quire::ast::{Ast, process as process_ast};
use regex;

use config::authorizers::Authorizer;
use config::root::{ConfigData, ConfigSource, Mixin};
use config::root::{config_validator, mixin_validator};
use super::Handler;
use config::static_files::Mode;
use config::log;
use intern::{LogFormatName, Authorizer as AuthorizerName, HandlerName};
use routing::RoutingTable;

quick_error! {
    #[derive(Debug)]
    pub enum Error {
        Io(err: io::Error) {
            display("IO error: {}", err)
            description("IO error")
            from()
        }
        Config(err: quire::ErrorList) {
            display("config error: {}", err)
            description("config error")
            from()
        }
        Validation(err: String) {
            display("validation error: {}", err)
            description("validation error")
            from()
        }
        Routing(err: String) {
            display("routing table error: {}", err)
            description("routing table error")
        }
        BadMixinPath(filename: PathBuf) {
            display("bad mixin path: {:?}, \
                only relative paths are allowed", filename)
            description("bad mixin path")
        }
        InvalidPrefixInMixin(prefix: String, filename: PathBuf,
                             typ: &'static str, name: String)
        {
            display("mixin {:?} has {} named {:?} without prefix {:?}",
                filename, typ, name, prefix)
            description("item in mixin file has invalid prefix")
        }
        MixinConflict(filename: PathBuf, typ: &'static str, name: String) {
            display("mixin {:?} has overlapping {} {:?}",
                filename, typ, name)
            description("mixin has overlapping item")
        }
        Regex(err: regex::Error) {
            display("error compiling routing regex: {}", err)
            description("error compiling routing regex")
            from()
        }
        NoHandler(name: HandlerName) {
            display("handler {:?} not found", name)
            description("handler not found")
        }
        NoAuthorizer(name: AuthorizerName) {
            display("authorizer {:?} not found", name)
            description("authorizer not found")
        }
    }
}

macro_rules! err {
    // Shortcut to config post-load validation error
    ($msg:expr, $($a:expr),*) => (
        return Err(format!($msg, $($a),*).into())
    )
}

fn join_filename(base: &Path, relative: &Path) -> Result<PathBuf, ()> {
    let mut path = PathBuf::from(&*base);
    path.pop(); // pop original filename
    for component in relative.components() {
        match component {
            Component::Normal(x) => path.push(x),
            _ => {
                return Err(());
            }
        }
    }
    Ok(path)
}

#[allow(dead_code)]
pub fn include_file(files: &RefCell<&mut Vec<(PathBuf, String, Metadata)>>,
    pos: &Pos, include: &Include,
    err: &ErrorCollector, options: &Options)
    -> Ast
{
    match *include {
        Include::File { filename } => {
            let path = match join_filename(&Path::new(&**pos.filename),
                                           &Path::new(filename))
            {
                Ok(path) => path,
                Err(()) => {
                    // TODO(tailhook) should this error exist?
                    err.add_error(quire::Error::preprocess_error(pos,
                        format!("Only relative paths without parent \
                                 directories can be included")));
                    return Ast::void(pos);
                }
            };

            debug!("{} Including {:?}", pos, path);

            let mut body = String::new();
            File::open(&path)
            .and_then(|mut f| {
                let m = f.metadata();
                f.read_to_string(&mut body)?;
                m
            })
            .map_err(|e| {
                err.add_error(quire::Error::open_error(&path, e))
            }).ok()
            .and_then(|metadata| {
                files.borrow_mut().push((
                    path.to_path_buf(),
                    String::from(filename),
                    metadata,
                ));
                parse_yaml(Rc::new(path.display().to_string()), &body,
                    |doc| { process_ast(&options, doc, err) },
                ).map_err(|e| err.add_error(e)).ok()
            })
            .unwrap_or_else(|| Ast::void(pos))
        }
    }
}

fn prefix_error<N: fmt::Display>(prefix: &str, filename: &Path,
    typ: &'static str, name: &N)
    -> Error
{
    Error::InvalidPrefixInMixin(prefix.to_string(), filename.to_path_buf(),
        typ, name.to_string())
}

fn conflict<N: fmt::Display>(filename: &Path, typ: &'static str, name: N)
    -> Error
{
    Error::MixinConflict(filename.to_path_buf(), typ, name.to_string())
}

fn mix_in<K, V>(
    filename: &Path, prefix: &str,
    dest: &mut HashMap<K, V>, src: HashMap<K, V>,
    typ: &'static str)
    -> Result<(), Error>
    where K: ::std::hash::Hash + ::std::ops::Deref<Target=str>,
          K: ::std::fmt::Display + Eq,
{
    for (h, handler) in src {
        if !(&*h).starts_with(prefix) {
            return Err(prefix_error(prefix, filename, typ, &h));
        }
        if dest.contains_key(&h) {
            return Err(conflict(filename, typ, &h));
        }
        dest.insert(h, handler);
    }
    Ok(())
}

pub fn read_config<P: AsRef<Path>>(filename: P)
    -> Result<(ConfigData, Vec<(PathBuf, String, Metadata)>), Error>
{
    let filename = filename.as_ref();
    let mut files = Vec::new();
    files.push((
        filename.to_path_buf(),
        String::from("<main>"),
        metadata(filename)?,
    ));
    let mut src: ConfigSource = {
        let cell = RefCell::new(&mut files);
        let mut opt = Options::default();
        opt.allow_include(
            |a, b, c, d| include_file(&cell, a, b, c, d));
        parse_config(filename, &config_validator(), &opt)?
    };

    for (prefix, ref incl) in &src.mixins {
        let incl_path = join_filename(filename, &Path::new(incl))
            .map_err(|()| Error::BadMixinPath(filename.to_path_buf()))?;
        files.push((
            incl_path.clone(),
            incl.display().to_string(),
            metadata(&incl_path)?,
        ));

        let mixin: Mixin = {
            let cell = RefCell::new(&mut files);
            let mut opt = Options::default();
            opt.allow_include(
                |a, b, c, d| include_file(&cell, a, b, c, d));
            parse_config(&incl_path, &mixin_validator(), &opt)?
        };
        mix_in(&incl_path, prefix,
               &mut src.handlers, mixin.handlers, "handler")?;
        mix_in(&incl_path, prefix,
               &mut src.authorizers, mixin.authorizers, "authorizer")?;
        mix_in(&incl_path, prefix,
               &mut src.session_pools, mixin.session_pools, "session-pool")?;
        mix_in(&incl_path, prefix, &mut src.http_destinations,
            mixin.http_destinations, "http-destination")?;
        mix_in(&incl_path, prefix, &mut src.ldap_destinations,
            mixin.ldap_destinations, "ldap-destination")?;
        mix_in(&incl_path, prefix,
            &mut src.networks, mixin.networks, "network")?;
        mix_in(&incl_path, prefix,
            &mut src.log_formats, mixin.log_formats, "log-format")?;
        mix_in(&incl_path, prefix,
            &mut src.disk_pools, mixin.disk_pools, "disk-pools")?;
    }
    return Ok((postprocess_config(src)?, files));
}

pub fn postprocess_config(mut src: ConfigSource)
    -> Result<ConfigData, Error>
{
    // Set some defaults
    if !src.log_formats.contains_key("debug-log") {
        src.log_formats.insert(LogFormatName::from("debug-log"),
            log::Format::from_string(r#"
                {{ request.client_ip }}
                {{ request.host }}
                "{{ request.method }}
                {{ request.path }}
                {{ request.version }}"
                {{ response.status_code }}
            "#.into()).expect("can always compile debug log"));
    }

    if !src.handlers.contains_key("default") {
        src.handlers.insert(HandlerName::from("default"), Handler::NotFound);
    }

    if !src.authorizers.contains_key("default") {
        src.authorizers.insert(AuthorizerName::from("default"),
                               Authorizer::AllowAll);
    }

    let mut cfg = ConfigData {
        routing: RoutingTable::new(&src)?,

        listen: src.listen,
        max_connections: src.max_connections,
        pipeline_depth: src.pipeline_depth,
        listen_error_timeout: src.listen_error_timeout,
        first_byte_timeout: src.first_byte_timeout,
        keep_alive_timeout: src.keep_alive_timeout,
        headers_timeout: src.headers_timeout,
        input_body_byte_timeout: src.input_body_byte_timeout,
        input_body_whole_timeout: src.input_body_whole_timeout,
        output_body_byte_timeout: src.output_body_byte_timeout,
        output_body_whole_timeout: src.output_body_whole_timeout,

        handlers: src.handlers,
        authorizers: src.authorizers,
        session_pools: src.session_pools,
        http_destinations: src.http_destinations,
        ldap_destinations: src.ldap_destinations,
        networks: src.networks,
        log_formats: src.log_formats,
        disk_pools: src.disk_pools,

        replication: src.replication,
        debug_routing: src.debug_routing,
        debug_logging: src.debug_logging,
        server_name: src.server_name,

        set_user: src.set_user,
        set_group: src.set_group,
    };

    // Extra config validations

    /* TODO
    for &(ref domain, ref sub) in cfg.routing.hosts() {
        for (path, route) in sub {
            if cfg.handlers.get(&route.destination).is_none() {
                err!("Unknown handler {:?}", route.destination)
            }
            if path.as_ref().map(|x| x.ends_with("/")).unwrap_or(false) {
                err!("Path must not end with /: {:?} {:?}",
                     domain, path);
            }
            if let Some(&Handler::StripWWWRedirect) =
                cfg.handlers.get(&route.destination)
            {
                if !domain.matches_www() {
                    err!(concat!("Expected `www.` prefix for StripWWWRedirect",
                                 " handler route: {:?} {:?}"), domain, path);
                }
            }
        }
    }
    */

    for (name, h) in &cfg.handlers {
        match h {
            &Handler::SwindonLattice(ref chat) => {
                match cfg.session_pools.get_mut(&chat.session_pool) {
                    None => {
                        err!("No session pool {:?} defined", chat.session_pool)
                    }
                    Some(mut pool) => {
                        let tangle = chat.use_tangle_prefix();
                        if pool.use_tangle_prefix.is_some() &&
                            pool.use_tangle_prefix != Some(tangle)
                        {
                            err!("Inconsistent `compatibility` for \
                                  pool {:?}", chat.session_pool);
                        }
                        Arc::make_mut(&mut pool)
                            .use_tangle_prefix = Some(tangle);

                        let dest = if tangle {
                            chat.message_handlers.resolve(
                                "tangle.session_inactive")
                        } else {
                            chat.message_handlers.resolve(
                                "swindon.session_inactive")
                        };

                        let tangle = chat.use_tangle_auth();
                        if pool.use_tangle_auth.is_some() &&
                            pool.use_tangle_auth != Some(tangle)
                        {
                            err!("Inconsistent `compatibility` for \
                                  pool {:?}", chat.session_pool);
                        }
                        Arc::make_mut(&mut pool)
                            .use_tangle_auth = Some(tangle);

                        let tangle = chat.weak_content_type();
                        if pool.weak_content_type.is_some() &&
                            pool.weak_content_type != Some(tangle)
                        {
                            err!("Inconsistent `compatibility` for \
                                  pool {:?}", chat.session_pool);
                        }
                        Arc::make_mut(&mut pool)
                            .weak_content_type = Some(tangle);

                        if !pool.inactivity_handlers.contains(dest) &&
                            pool.inactivity_handlers.len() != 0 {
                            err!(concat!(
                                "Inactivity destinations mismatch for",
                                 "{:?}: {:?}"), name, dest)
                        }
                    }
                }
                if let Some(h) = chat.http_route.as_ref() {
                    if !cfg.handlers.contains_key(h) {
                        err!("{:?}: unknown http route {:?}", name, h)
                    }
                }
                let u = &chat.message_handlers.default.upstream;
                if let Some(http_dest) = cfg.http_destinations.get(u) {
                    if http_dest.override_host_header.is_none() {
                        err!("http destination {:?} is used \
                             in message-handler of {:?}, so must contain \
                             override-host-header setting.", u, name);
                    }
                } else {
                    err!("{:?}: unknown http destination {:?}", name, u)
                }
                for (_, dest) in &chat.message_handlers.map {
                    if !cfg.http_destinations.contains_key(&dest.upstream) {
                        err!("{:?}: unknown http destination {:?}",
                             name, dest.upstream)
                    }
                }
            }
            &Handler::Proxy(ref proxy) => {
                let u = &proxy.destination.upstream;
                if !cfg.http_destinations.contains_key(u) {
                    err!("{:?}: unknown http destination {:?}", name, u)
                }
                if proxy.request_id_header.is_some() {
                    warn!(concat!(
                        "{:?}: request_id_header is deprecated",
                        " in !Proxy setting, it must be specified",
                        " in http destination"), name);
                }
            }
            &Handler::Static(ref config) => {
                if config.strip_host_suffix.is_some() &&
                   config.mode != Mode::with_hostname
                {
                    err!("{:?}: `strip-host-suffix` only \
                        works when `mode: with-hostname`", name);
                }
            }
            _ => {}
        }
    }
    // TODO: verify session_pool inactivity handlers
    for (name, s) in &cfg.session_pools {
        for dest in &s.inactivity_handlers {
            if !cfg.http_destinations.contains_key(&dest.upstream) {
                err!("{:?}: unknown http destination {:?}",
                     name, dest.upstream)
            }
        }
    }

    Ok(cfg)
}
<|endoftext|>


<commit_before>// Copyright (c) 2014-2016 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for sending and receiving data link layer packets using the WinPcap library.

extern crate libc;
extern crate winapi;

use bindings::{bpf, winpcap};
use {DataLinkReceiver, DataLinkSender, MacAddr, NetworkInterface};

use ipnetwork::{ip_mask_to_prefix, IpNetwork};

use std::cmp;
use std::collections::VecDeque;
use std::ffi::{CStr, CString};
use std::io;
use std::mem;
use std::slice;
use std::str::from_utf8_unchecked;
use std::sync::Arc;

struct WinPcapAdapter {
    adapter: winpcap::LPADAPTER,
}

impl Drop for WinPcapAdapter {
    fn drop(&mut self) {
        unsafe {
            winpcap::PacketCloseAdapter(self.adapter);
        }
    }
}

struct WinPcapPacket {
    packet: winpcap::LPPACKET,
}

impl Drop for WinPcapPacket {
    fn drop(&mut self) {
        unsafe {
            winpcap::PacketFreePacket(self.packet);
        }
    }
}

/// The WinPcap's specific configuration.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Config {
    /// The size of buffer to use when writing packets. Defaults to 4096.
    pub write_buffer_size: usize,

    /// The size of buffer to use when reading packets. Defaults to 4096.
    pub read_buffer_size: usize,
}

impl<'a> From<&'a super::Config> for Config {
    fn from(config: &super::Config) -> Config {
        Config {
            write_buffer_size: config.write_buffer_size,
            read_buffer_size: config.read_buffer_size,
        }
    }
}

impl Default for Config {
    fn default() -> Config {
        Config {
            write_buffer_size: 4096,
            read_buffer_size: 4096,
        }
    }
}

/// Create a datalink channel using the WinPcap library.
#[inline]
pub fn channel(network_interface: &NetworkInterface, config: Config) -> io::Result<super::Channel> {
    let mut read_buffer = Vec::new();
    read_buffer.resize(config.read_buffer_size, 0u8);

    let mut write_buffer = Vec::new();
    write_buffer.resize(config.write_buffer_size, 0u8);

    let adapter = unsafe {
        let net_if_str = CString::new(network_interface.name.as_bytes()).unwrap();
        winpcap::PacketOpenAdapter(net_if_str.as_ptr() as *mut libc::c_char)
    };
    if adapter.is_null() {
        return Err(io::Error::last_os_error());
    }

    let ret = unsafe { winpcap::PacketSetHwFilter(adapter, winpcap::NDIS_PACKET_TYPE_PROMISCUOUS) };
    if ret == 0 {
        return Err(io::Error::last_os_error());
    }

    // Set kernel buffer size
    let ret = unsafe { winpcap::PacketSetBuff(adapter, config.read_buffer_size as libc::c_int) };
    if ret == 0 {
        return Err(io::Error::last_os_error());
    }

    // Immediate mode
    let ret = unsafe { winpcap::PacketSetMinToCopy(adapter, 1) };
    if ret == 0 {
        return Err(io::Error::last_os_error());
    }

    let read_packet = unsafe { winpcap::PacketAllocatePacket() };
    if read_packet.is_null() {
        unsafe {
            winpcap::PacketCloseAdapter(adapter);
        }
        return Err(io::Error::last_os_error());
    }

    unsafe {
        winpcap::PacketInitPacket(
            read_packet,
            read_buffer.as_mut_ptr() as winpcap::PVOID,
            config.read_buffer_size as winpcap::UINT,
        )
    }

    let write_packet = unsafe { winpcap::PacketAllocatePacket() };
    if write_packet.is_null() {
        unsafe {
            winpcap::PacketFreePacket(read_packet);
            winpcap::PacketCloseAdapter(adapter);
        }
        return Err(io::Error::last_os_error());
    }

    unsafe {
        winpcap::PacketInitPacket(
            write_packet,
            write_buffer.as_mut_ptr() as winpcap::PVOID,
            config.write_buffer_size as winpcap::UINT,
        )
    }

    let adapter = Arc::new(WinPcapAdapter { adapter: adapter });
    let sender = Box::new(DataLinkSenderImpl {
        adapter: adapter.clone(),
        _write_buffer: write_buffer,
        packet: WinPcapPacket {
            packet: write_packet,
        },
    });
    let receiver = Box::new(DataLinkReceiverImpl {
        adapter: adapter,
        _read_buffer: read_buffer,
        packet: WinPcapPacket {
            packet: read_packet,
        },
        // Enough room for minimally sized packets without reallocating
        packets: VecDeque::with_capacity(unsafe { (*read_packet).Length } as usize / 64),
    });
    Ok(super::Channel::Ethernet(sender, receiver))
}

struct DataLinkSenderImpl {
    adapter: Arc<WinPcapAdapter>,
    _write_buffer: Vec<u8>,
    packet: WinPcapPacket,
}

impl DataLinkSender for DataLinkSenderImpl {
    #[inline]
    fn build_and_send(
        &mut self,
        num_packets: usize,
        packet_size: usize,
        func: &mut dyn FnMut(&mut [u8]),
    ) -> Option<io::Result<()>> {
        let len = num_packets * packet_size;
        if len >= unsafe { (*self.packet.packet).Length } as usize {
            None
        } else {
            let min = unsafe { cmp::min((*self.packet.packet).Length as usize, len) };
            let slice: &mut [u8] =
                unsafe { slice::from_raw_parts_mut((*self.packet.packet).Buffer as *mut u8, min) };
            for chunk in slice.chunks_mut(packet_size) {
                func(chunk);

                // Make sure the right length of packet is sent
                let old_len = unsafe { (*self.packet.packet).Length };
                unsafe {
                    (*self.packet.packet).Length = packet_size as u32;
                }

                let ret = unsafe {
                    winpcap::PacketSendPacket(self.adapter.adapter, self.packet.packet, 0)
                };

                unsafe {
                    (*self.packet.packet).Length = old_len;
                }

                if ret == 0 {
                    return Some(Err(io::Error::last_os_error()));
                }
            }
            Some(Ok(()))
        }
    }

    #[inline]
    fn send_to(&mut self, packet: &[u8], _dst: Option<NetworkInterface>) -> Option<io::Result<()>> {
        self.build_and_send(1, packet.len(), &mut |eh: &mut [u8]| {
            eh.copy_from_slice(packet);
        })
    }
}

unsafe impl Send for DataLinkSenderImpl {}
unsafe impl Sync for DataLinkSenderImpl {}

struct DataLinkReceiverImpl {
    adapter: Arc<WinPcapAdapter>,
    _read_buffer: Vec<u8>,
    packet: WinPcapPacket,
    packets: VecDeque<(usize, usize)>,
}

unsafe impl Send for DataLinkReceiverImpl {}
unsafe impl Sync for DataLinkReceiverImpl {}

impl DataLinkReceiver for DataLinkReceiverImpl {
    fn next(&mut self) -> io::Result<&[u8]> {
        // NOTE Most of the logic here is identical to FreeBSD/OS X
        while self.packets.is_empty() {
            let ret = unsafe {
                winpcap::PacketReceivePacket(self.adapter.adapter, self.packet.packet, 0)
            };
            let buflen = match ret {
                0 => return Err(io::Error::last_os_error()),
                _ => unsafe { (*self.packet.packet).ulBytesReceived as isize },
            };
            let mut ptr = unsafe { (*self.packet.packet).Buffer };
            let end = unsafe { (*self.packet.packet).Buffer.offset(buflen) };
            while ptr < end {
                unsafe {
                    let packet: *const bpf::bpf_hdr = mem::transmute(ptr);
                    let start = ptr as isize + (*packet).bh_hdrlen as isize
                        - (*self.packet.packet).Buffer as isize;
                    self.packets
                        .push_back((start as usize, (*packet).bh_caplen as usize));
                    let offset = (*packet).bh_hdrlen as isize + (*packet).bh_caplen as isize;
                    ptr = ptr.offset(bpf::BPF_WORDALIGN(offset));
                }
            }
        }
        let (start, len) = self.packets.pop_front().unwrap();
        let slice = unsafe {
            let data = (*self.packet.packet).Buffer as usize + start;
            slice::from_raw_parts(data as *const u8, len)
        };
        Ok(slice)
    }
}

/// Get a list of available network interfaces for the current machine.
pub fn interfaces() -> Vec<NetworkInterface> {
    use bindings::winpcap;

    let mut adapters_size = 0u32;

    unsafe {
        let mut tmp: winpcap::IP_ADAPTER_INFO = mem::zeroed();
        // FIXME [windows] This only gets IPv4 addresses - should use
        // GetAdaptersAddresses
        winpcap::GetAdaptersInfo(&mut tmp, &mut adapters_size);
    }

    let mut vec_size = adapters_size / mem::size_of::<winpcap::IP_ADAPTER_INFO>() as u32;
    if adapters_size % mem::size_of::<winpcap::IP_ADAPTER_INFO>() as u32 != 0 {
        vec_size += 1;
    }
    let mut adapters = Vec::with_capacity(vec_size as usize);

    // FIXME [windows] Check return code
    unsafe {
        winpcap::GetAdaptersInfo(adapters.as_mut_ptr(), &mut adapters_size);
    }

    // Create a complete list of NetworkInterfaces for the machine
    let mut cursor = adapters.as_mut_ptr();
    let mut all_ifaces = Vec::with_capacity(vec_size as usize);
    while !cursor.is_null() {
        let mac = unsafe {
            MacAddr(
                (*cursor).Address[0],
                (*cursor).Address[1],
                (*cursor).Address[2],
                (*cursor).Address[3],
                (*cursor).Address[4],
                (*cursor).Address[5],
            )
        };
        let mut ip_cursor = unsafe { &mut (*cursor).IpAddressList as winpcap::PIP_ADDR_STRING };
        let mut ips = Vec::new();
        while !ip_cursor.is_null() {
            if let Ok(ip_network) = parse_ip_network(ip_cursor) {
                ips.push(ip_network);
            }
            ip_cursor = unsafe { (*ip_cursor).Next };
        }

        unsafe {
            let name_str_ptr = (*cursor).AdapterName.as_ptr() as *const i8;

            let bytes = CStr::from_ptr(name_str_ptr).to_bytes();
            let name_str = from_utf8_unchecked(bytes).to_owned();

            all_ifaces.push(NetworkInterface {
                name: name_str,
                index: (*cursor).Index,
                mac: Some(mac),
                ips: ips,
                // flags: (*cursor).Type, // FIXME [windows]
                flags: 0,
            });

            cursor = (*cursor).Next;
        }
    }

    let mut buf = vec![0u8; 4096];
    let mut buflen = buf.len() as u32;

    if unsafe { winpcap::PacketGetAdapterNames(buf.as_mut_ptr() as *mut i8, &mut buflen) } == 0 {
        buf.resize(buflen as usize, 0);

        // Second call should now work with the correct buffer size. If not, this may be
        // due to some privilege or other unforseen issue.
        if unsafe { winpcap::PacketGetAdapterNames(buf.as_mut_ptr() as *mut i8, &mut buflen) } == 0
        {
            panic!("Unable to get interface list despite increasing buffer size");
        }
    }

    let buf_str = unsafe { from_utf8_unchecked(&buf) };
    let iface_names = buf_str.split("\0\0").next();
    let mut vec = Vec::new();

    // Return only supported adapters
    match iface_names {
        Some(iface_names) => {
            for iface in iface_names.split('\0') {
                let name = iface.to_owned();
                let next = all_ifaces
                    .iter()
                    .filter(|x| name[..].ends_with(&x.name[..]))
                    .next();
                if next.is_some() {
                    let mut iface = next.unwrap().clone();
                    iface.name = name;
                    vec.push(iface);
                }
            }
        }
        None => (),
    };

    vec
}

fn parse_ip_network(ip_cursor: winpcap::PIP_ADDR_STRING) -> Result<IpNetwork, ()> {
    let ip_str_ptr = unsafe { &(*ip_cursor) }.IpAddress.String.as_ptr() as *const i8;
    let ip_bytes = unsafe { CStr::from_ptr(ip_str_ptr).to_bytes() };
    let ip_str = unsafe { from_utf8_unchecked(ip_bytes).to_owned() };
    let ip = ip_str.parse().map_err(|_| ())?;

    let mask_str_ptr = unsafe { &(*ip_cursor) }.IpMask.String.as_ptr() as *const i8;
    let mask_bytes = unsafe { CStr::from_ptr(mask_str_ptr).to_bytes() };
    let mask_str = unsafe { from_utf8_unchecked(mask_bytes).to_owned() };
    let mask = mask_str.parse().map_err(|_| ())?;

    let prefix = ip_mask_to_prefix(mask).map_err(|_| ())?;
    IpNetwork::new(ip, prefix).map_err(|_| ())
}
<commit_msg>Remove redundant use bindings::winpcap; from winpcap.rs<commit_after>// Copyright (c) 2014-2016 Robert Clipsham <robert@octarineparrot.com>
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

//! Support for sending and receiving data link layer packets using the WinPcap library.

extern crate libc;
extern crate winapi;

use bindings::{bpf, winpcap};
use {DataLinkReceiver, DataLinkSender, MacAddr, NetworkInterface};

use ipnetwork::{ip_mask_to_prefix, IpNetwork};

use std::cmp;
use std::collections::VecDeque;
use std::ffi::{CStr, CString};
use std::io;
use std::mem;
use std::slice;
use std::str::from_utf8_unchecked;
use std::sync::Arc;

struct WinPcapAdapter {
    adapter: winpcap::LPADAPTER,
}

impl Drop for WinPcapAdapter {
    fn drop(&mut self) {
        unsafe {
            winpcap::PacketCloseAdapter(self.adapter);
        }
    }
}

struct WinPcapPacket {
    packet: winpcap::LPPACKET,
}

impl Drop for WinPcapPacket {
    fn drop(&mut self) {
        unsafe {
            winpcap::PacketFreePacket(self.packet);
        }
    }
}

/// The WinPcap's specific configuration.
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq)]
pub struct Config {
    /// The size of buffer to use when writing packets. Defaults to 4096.
    pub write_buffer_size: usize,

    /// The size of buffer to use when reading packets. Defaults to 4096.
    pub read_buffer_size: usize,
}

impl<'a> From<&'a super::Config> for Config {
    fn from(config: &super::Config) -> Config {
        Config {
            write_buffer_size: config.write_buffer_size,
            read_buffer_size: config.read_buffer_size,
        }
    }
}

impl Default for Config {
    fn default() -> Config {
        Config {
            write_buffer_size: 4096,
            read_buffer_size: 4096,
        }
    }
}

/// Create a datalink channel using the WinPcap library.
#[inline]
pub fn channel(network_interface: &NetworkInterface, config: Config) -> io::Result<super::Channel> {
    let mut read_buffer = Vec::new();
    read_buffer.resize(config.read_buffer_size, 0u8);

    let mut write_buffer = Vec::new();
    write_buffer.resize(config.write_buffer_size, 0u8);

    let adapter = unsafe {
        let net_if_str = CString::new(network_interface.name.as_bytes()).unwrap();
        winpcap::PacketOpenAdapter(net_if_str.as_ptr() as *mut libc::c_char)
    };
    if adapter.is_null() {
        return Err(io::Error::last_os_error());
    }

    let ret = unsafe { winpcap::PacketSetHwFilter(adapter, winpcap::NDIS_PACKET_TYPE_PROMISCUOUS) };
    if ret == 0 {
        return Err(io::Error::last_os_error());
    }

    // Set kernel buffer size
    let ret = unsafe { winpcap::PacketSetBuff(adapter, config.read_buffer_size as libc::c_int) };
    if ret == 0 {
        return Err(io::Error::last_os_error());
    }

    // Immediate mode
    let ret = unsafe { winpcap::PacketSetMinToCopy(adapter, 1) };
    if ret == 0 {
        return Err(io::Error::last_os_error());
    }

    let read_packet = unsafe { winpcap::PacketAllocatePacket() };
    if read_packet.is_null() {
        unsafe {
            winpcap::PacketCloseAdapter(adapter);
        }
        return Err(io::Error::last_os_error());
    }

    unsafe {
        winpcap::PacketInitPacket(
            read_packet,
            read_buffer.as_mut_ptr() as winpcap::PVOID,
            config.read_buffer_size as winpcap::UINT,
        )
    }

    let write_packet = unsafe { winpcap::PacketAllocatePacket() };
    if write_packet.is_null() {
        unsafe {
            winpcap::PacketFreePacket(read_packet);
            winpcap::PacketCloseAdapter(adapter);
        }
        return Err(io::Error::last_os_error());
    }

    unsafe {
        winpcap::PacketInitPacket(
            write_packet,
            write_buffer.as_mut_ptr() as winpcap::PVOID,
            config.write_buffer_size as winpcap::UINT,
        )
    }

    let adapter = Arc::new(WinPcapAdapter { adapter: adapter });
    let sender = Box::new(DataLinkSenderImpl {
        adapter: adapter.clone(),
        _write_buffer: write_buffer,
        packet: WinPcapPacket {
            packet: write_packet,
        },
    });
    let receiver = Box::new(DataLinkReceiverImpl {
        adapter: adapter,
        _read_buffer: read_buffer,
        packet: WinPcapPacket {
            packet: read_packet,
        },
        // Enough room for minimally sized packets without reallocating
        packets: VecDeque::with_capacity(unsafe { (*read_packet).Length } as usize / 64),
    });
    Ok(super::Channel::Ethernet(sender, receiver))
}

struct DataLinkSenderImpl {
    adapter: Arc<WinPcapAdapter>,
    _write_buffer: Vec<u8>,
    packet: WinPcapPacket,
}

impl DataLinkSender for DataLinkSenderImpl {
    #[inline]
    fn build_and_send(
        &mut self,
        num_packets: usize,
        packet_size: usize,
        func: &mut dyn FnMut(&mut [u8]),
    ) -> Option<io::Result<()>> {
        let len = num_packets * packet_size;
        if len >= unsafe { (*self.packet.packet).Length } as usize {
            None
        } else {
            let min = unsafe { cmp::min((*self.packet.packet).Length as usize, len) };
            let slice: &mut [u8] =
                unsafe { slice::from_raw_parts_mut((*self.packet.packet).Buffer as *mut u8, min) };
            for chunk in slice.chunks_mut(packet_size) {
                func(chunk);

                // Make sure the right length of packet is sent
                let old_len = unsafe { (*self.packet.packet).Length };
                unsafe {
                    (*self.packet.packet).Length = packet_size as u32;
                }

                let ret = unsafe {
                    winpcap::PacketSendPacket(self.adapter.adapter, self.packet.packet, 0)
                };

                unsafe {
                    (*self.packet.packet).Length = old_len;
                }

                if ret == 0 {
                    return Some(Err(io::Error::last_os_error()));
                }
            }
            Some(Ok(()))
        }
    }

    #[inline]
    fn send_to(&mut self, packet: &[u8], _dst: Option<NetworkInterface>) -> Option<io::Result<()>> {
        self.build_and_send(1, packet.len(), &mut |eh: &mut [u8]| {
            eh.copy_from_slice(packet);
        })
    }
}

unsafe impl Send for DataLinkSenderImpl {}
unsafe impl Sync for DataLinkSenderImpl {}

struct DataLinkReceiverImpl {
    adapter: Arc<WinPcapAdapter>,
    _read_buffer: Vec<u8>,
    packet: WinPcapPacket,
    packets: VecDeque<(usize, usize)>,
}

unsafe impl Send for DataLinkReceiverImpl {}
unsafe impl Sync for DataLinkReceiverImpl {}

impl DataLinkReceiver for DataLinkReceiverImpl {
    fn next(&mut self) -> io::Result<&[u8]> {
        // NOTE Most of the logic here is identical to FreeBSD/OS X
        while self.packets.is_empty() {
            let ret = unsafe {
                winpcap::PacketReceivePacket(self.adapter.adapter, self.packet.packet, 0)
            };
            let buflen = match ret {
                0 => return Err(io::Error::last_os_error()),
                _ => unsafe { (*self.packet.packet).ulBytesReceived as isize },
            };
            let mut ptr = unsafe { (*self.packet.packet).Buffer };
            let end = unsafe { (*self.packet.packet).Buffer.offset(buflen) };
            while ptr < end {
                unsafe {
                    let packet: *const bpf::bpf_hdr = mem::transmute(ptr);
                    let start = ptr as isize + (*packet).bh_hdrlen as isize
                        - (*self.packet.packet).Buffer as isize;
                    self.packets
                        .push_back((start as usize, (*packet).bh_caplen as usize));
                    let offset = (*packet).bh_hdrlen as isize + (*packet).bh_caplen as isize;
                    ptr = ptr.offset(bpf::BPF_WORDALIGN(offset));
                }
            }
        }
        let (start, len) = self.packets.pop_front().unwrap();
        let slice = unsafe {
            let data = (*self.packet.packet).Buffer as usize + start;
            slice::from_raw_parts(data as *const u8, len)
        };
        Ok(slice)
    }
}

/// Get a list of available network interfaces for the current machine.
pub fn interfaces() -> Vec<NetworkInterface> {
    let mut adapters_size = 0u32;

    unsafe {
        let mut tmp: winpcap::IP_ADAPTER_INFO = mem::zeroed();
        // FIXME [windows] This only gets IPv4 addresses - should use
        // GetAdaptersAddresses
        winpcap::GetAdaptersInfo(&mut tmp, &mut adapters_size);
    }

    let mut vec_size = adapters_size / mem::size_of::<winpcap::IP_ADAPTER_INFO>() as u32;
    if adapters_size % mem::size_of::<winpcap::IP_ADAPTER_INFO>() as u32 != 0 {
        vec_size += 1;
    }
    let mut adapters = Vec::with_capacity(vec_size as usize);

    // FIXME [windows] Check return code
    unsafe {
        winpcap::GetAdaptersInfo(adapters.as_mut_ptr(), &mut adapters_size);
    }

    // Create a complete list of NetworkInterfaces for the machine
    let mut cursor = adapters.as_mut_ptr();
    let mut all_ifaces = Vec::with_capacity(vec_size as usize);
    while !cursor.is_null() {
        let mac = unsafe {
            MacAddr(
                (*cursor).Address[0],
                (*cursor).Address[1],
                (*cursor).Address[2],
                (*cursor).Address[3],
                (*cursor).Address[4],
                (*cursor).Address[5],
            )
        };
        let mut ip_cursor = unsafe { &mut (*cursor).IpAddressList as winpcap::PIP_ADDR_STRING };
        let mut ips = Vec::new();
        while !ip_cursor.is_null() {
            if let Ok(ip_network) = parse_ip_network(ip_cursor) {
                ips.push(ip_network);
            }
            ip_cursor = unsafe { (*ip_cursor).Next };
        }

        unsafe {
            let name_str_ptr = (*cursor).AdapterName.as_ptr() as *const i8;

            let bytes = CStr::from_ptr(name_str_ptr).to_bytes();
            let name_str = from_utf8_unchecked(bytes).to_owned();

            all_ifaces.push(NetworkInterface {
                name: name_str,
                index: (*cursor).Index,
                mac: Some(mac),
                ips: ips,
                // flags: (*cursor).Type, // FIXME [windows]
                flags: 0,
            });

            cursor = (*cursor).Next;
        }
    }

    let mut buf = vec![0u8; 4096];
    let mut buflen = buf.len() as u32;

    if unsafe { winpcap::PacketGetAdapterNames(buf.as_mut_ptr() as *mut i8, &mut buflen) } == 0 {
        buf.resize(buflen as usize, 0);

        // Second call should now work with the correct buffer size. If not, this may be
        // due to some privilege or other unforseen issue.
        if unsafe { winpcap::PacketGetAdapterNames(buf.as_mut_ptr() as *mut i8, &mut buflen) } == 0
        {
            panic!("Unable to get interface list despite increasing buffer size");
        }
    }

    let buf_str = unsafe { from_utf8_unchecked(&buf) };
    let iface_names = buf_str.split("\0\0").next();
    let mut vec = Vec::new();

    // Return only supported adapters
    match iface_names {
        Some(iface_names) => {
            for iface in iface_names.split('\0') {
                let name = iface.to_owned();
                let next = all_ifaces
                    .iter()
                    .filter(|x| name[..].ends_with(&x.name[..]))
                    .next();
                if next.is_some() {
                    let mut iface = next.unwrap().clone();
                    iface.name = name;
                    vec.push(iface);
                }
            }
        }
        None => (),
    };

    vec
}

fn parse_ip_network(ip_cursor: winpcap::PIP_ADDR_STRING) -> Result<IpNetwork, ()> {
    let ip_str_ptr = unsafe { &(*ip_cursor) }.IpAddress.String.as_ptr() as *const i8;
    let ip_bytes = unsafe { CStr::from_ptr(ip_str_ptr).to_bytes() };
    let ip_str = unsafe { from_utf8_unchecked(ip_bytes).to_owned() };
    let ip = ip_str.parse().map_err(|_| ())?;

    let mask_str_ptr = unsafe { &(*ip_cursor) }.IpMask.String.as_ptr() as *const i8;
    let mask_bytes = unsafe { CStr::from_ptr(mask_str_ptr).to_bytes() };
    let mask_str = unsafe { from_utf8_unchecked(mask_bytes).to_owned() };
    let mask = mask_str.parse().map_err(|_| ())?;

    let prefix = ip_mask_to_prefix(mask).map_err(|_| ())?;
    IpNetwork::new(ip, prefix).map_err(|_| ())
}
<|endoftext|>