use std::{convert, error, fmt, fs, io, net, slice, result};
use std::collections::HashMap;
use std::net::IpAddr;
use std::path::Path;
use std::str::FromStr;
use domain::bits::name::{self, DNameSlice, DNameBuf};
use super::HostEnt;
pub fn get_host_by_name<N: AsRef<DNameSlice>>(name: N)
-> io::Result<Option<HostEnt>> {
let hosts = Hosts::default();
match hosts.lookup_host(name.as_ref()) {
Some(iter) => {
Ok(Some(HostEnt {
name: format!("{}", name.as_ref()),
aliases: Vec::new(),
addrs: iter.map(|addr| *addr).collect(),
}))
}
None => Ok(None)
}
}
pub fn get_host_by_addr(addr: IpAddr) -> io::Result<Option<HostEnt>> {
let hosts = Hosts::default();
match hosts.lookup_addr(addr) {
Some(mut iter) => {
let name = match iter.next() {
None => return Ok(None),
Some(name) => format!("{}", name)
};
Ok(Some(HostEnt {
name: name,
aliases: iter.map(|n| format!("{}", n)).collect(),
addrs: vec![addr],
}))
}
None => Ok(None)
}
}
#[derive(Clone, Debug, Default)]
pub struct Hosts {
forward: HashMap<DNameBuf, Vec<IpAddr>>,
reverse: HashMap<IpAddr, Vec<DNameBuf>>,
}
impl Hosts {
pub fn new() -> Self {
Hosts {
forward: HashMap::new(),
reverse: HashMap::new()
}
}
pub fn default() -> Self {
let mut res = Hosts::new();
let _ = res.parse_file("/etc/hosts");
res
}
pub fn add_forward(&mut self, name: &DNameBuf, addr: IpAddr) {
if let Some(ref mut vec) = self.forward.get_mut(name) {
vec.push(addr);
return;
}
self.forward.insert(name.clone(), vec!(addr));
}
pub fn add_reverse(&mut self, addr: IpAddr, name: DNameBuf) {
if let Some(ref mut vec) = self.reverse.get_mut(&addr) {
vec.push(name);
return;
}
self.reverse.insert(addr, vec!(name));
}
}
impl Hosts {
pub fn lookup_host<N: AsRef<DNameSlice>>(&self, name: N)
-> Option<slice::Iter<IpAddr>> {
self._lookup_host(name.as_ref())
}
fn _lookup_host(&self, name: &DNameSlice) -> Option<slice::Iter<IpAddr>> {
self.forward.get(name).map(|vec| vec.iter())
}
pub fn lookup_addr(&self, addr: IpAddr) -> Option<slice::Iter<DNameBuf>> {
self.reverse.get(&addr).map(|vec| vec.iter())
}
}
impl Hosts {
pub fn parse_file<P: AsRef<Path>>(&mut self, path: P) -> Result<()> {
let mut file = try!(fs::File::open(path));
self.parse(&mut file)
}
pub fn parse<R: io::Read>(&mut self, reader: &mut R) -> Result<()> {
use std::io::BufRead;
for line in io::BufReader::new(reader).lines() {
let _ = self.parse_line(try!(line));
}
Ok(())
}
fn parse_line(&mut self, line: String) -> Result<()> {
let line: &str = match line.find('#') {
Some(pos) => line.split_at(pos).0,
None => &line
};
let line = line.trim();
if line.is_empty() { return Ok(()) }
let mut words = line.split_whitespace();
let addr = try!(words.next().ok_or(Error::ParseError));
let addr = try!(IpAddr::from_str(addr));
let cname = try!(words.next().ok_or(Error::ParseError));
let cname = try!(DNameBuf::from_str(cname));
self.add_forward(&cname, addr);
self.add_reverse(addr, cname);
for name in words {
let name = try!(DNameBuf::from_str(name));
self.add_forward(&name, addr);
}
Ok(())
}
}
#[derive(Debug)]
pub enum Error {
ParseError,
IoError(io::Error),
}
impl error::Error for Error {
fn description(&self) -> &str {
match *self {
Error::ParseError => "error parsing configuration",
Error::IoError(ref e) => e.description(),
}
}
}
impl convert::From<io::Error> for Error {
fn from(error: io::Error) -> Error {
Error::IoError(error)
}
}
impl convert::From<name::FromStrError> for Error {
fn from(_: name::FromStrError) -> Error {
Error::ParseError
}
}
impl convert::From<::std::num::ParseIntError> for Error {
fn from(_: ::std::num::ParseIntError) -> Error {
Error::ParseError
}
}
impl convert::From<net::AddrParseError> for Error {
fn from(_: net::AddrParseError) -> Error {
Error::ParseError
}
}
impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
use std::error::Error;
self.description().fmt(f)
}
}
pub type Result<T> = result::Result<T, Error>;