use std::fs::File;
use std::io::{self, Read};
use std::net::IpAddr;
use std::path::{Path, PathBuf};
#[derive(Clone, Debug)]
pub struct HostTable {
pub hosts: Vec<Host>,
}
impl HostTable {
pub fn find_address(&self, name: &str) -> Option<IpAddr> {
self.find_host_by_name(name).map(|h| h.address)
}
pub fn find_name(&self, addr: IpAddr) -> Option<&str> {
self.find_host_by_address(addr).map(|h| &h.name[..])
}
pub fn find_host_by_address(&self, addr: IpAddr) -> Option<&Host> {
self.hosts.iter().find(|h| h.address == addr)
}
pub fn find_host_by_name(&self, name: &str) -> Option<&Host> {
self.hosts.iter().find(|h| h.name == name ||
h.aliases.iter().any(|a| a == name))
}
}
#[derive(Clone, Debug)]
pub struct Host {
pub address: IpAddr,
pub name: String,
pub aliases: Vec<String>,
}
pub fn host_file() -> PathBuf {
host_file_impl()
}
#[cfg(unix)]
fn host_file_impl() -> PathBuf {
PathBuf::from("/etc/hosts")
}
#[cfg(windows)]
fn host_file_impl() -> PathBuf {
use std::env::var_os;
match var_os("SystemRoot") {
Some(root) => PathBuf::from(root).join("System32/drivers/etc/hosts"),
None => PathBuf::from("C:/Windows/System32/drivers/etc/hosts")
}
}
pub fn load_hosts(path: &Path) -> io::Result<HostTable> {
let mut f = try!(File::open(path));
let mut buf = String::new();
try!(f.read_to_string(&mut buf));
parse_host_table(&buf)
}
pub fn parse_host_table(data: &str) -> io::Result<HostTable> {
let mut hosts = Vec::new();
for line in data.lines() {
let mut line = line;
if let Some(pos) = line.find('#') {
line = &line[..pos];
}
let mut words = line.split_whitespace();
let addr_str = match words.next() {
Some(w) => w,
None => continue
};
let addr = match addr_str.parse() {
Ok(addr) => addr,
Err(_) => return Err(io::Error::new(io::ErrorKind::InvalidData,
format!("invalid address: {}", addr_str)))
};
let name = match words.next() {
Some(w) => w,
None => return Err(io::Error::new(io::ErrorKind::InvalidData,
"missing names"))
};
hosts.push(Host{
address: addr,
name: name.to_owned(),
aliases: words.map(|s| s.to_owned()).collect(),
});
}
Ok(HostTable{hosts: hosts})
}
#[cfg(test)]
mod test {
use super::parse_host_table;
use std::net::IpAddr;
fn ip(s: &str) -> IpAddr {
s.parse().unwrap()
}
#[test]
fn test_hosts() {
let hosts = parse_host_table("\
# Comment line
127.0.0.1 localhost
::1 ip6-localhost
192.168.10.1 foo foo.bar foo.local # Mid-line comment
").unwrap();
assert_eq!(hosts.find_address("localhost"), Some(ip("127.0.0.1")));
assert_eq!(hosts.find_address("ip6-localhost"), Some(ip("::1")));
assert_eq!(hosts.find_name(ip("192.168.10.1")), Some("foo"));
assert_eq!(hosts.find_address("missing"), None);
assert_eq!(hosts.find_name(ip("0.0.0.0")), None);
let host = hosts.find_host_by_address(ip("192.168.10.1")).unwrap();
assert_eq!(host.address, ip("192.168.10.1"));
assert_eq!(host.name, "foo");
assert_eq!(host.aliases, ["foo.bar", "foo.local"]);
}
}