cdns_rs/
cfg_host_parser.rsuse std::borrow::Borrow;
use std::collections::HashSet;
use std::hash::{Hash, Hasher};
use std::net::IpAddr;
use crate::a_sync::QType;
use crate::a_sync::common::HOST_CFG_PATH;
use crate::{error::*, writer_error};
use crate::tokenizer::*;
#[derive(Clone, Debug)]
pub struct HostnameEntry
{
ip: IpAddr,
hostnames: Vec<String>,
}
impl Eq for HostnameEntry {}
impl PartialEq for HostnameEntry
{
fn eq(&self, other: &HostnameEntry) -> bool
{
return self.ip == other.ip;
}
}
impl Borrow<IpAddr> for HostnameEntry
{
fn borrow(&self) -> &IpAddr
{
return &self.ip;
}
}
impl Hash for HostnameEntry
{
fn hash<H: Hasher>(&self, state: &mut H)
{
self.ip.hash(state);
}
}
impl HostnameEntry
{
pub
fn get_ip(&self) -> &IpAddr
{
return &self.ip;
}
pub
fn get_hostnames(&self) -> &[String]
{
return self.hostnames.as_slice();
}
pub
fn get_hostnames_iter(&self) -> std::slice::Iter<'_, String>
{
return self.hostnames.iter();
}
}
#[derive(Clone, Debug)]
pub struct HostConfig
{
hostnames: HashSet<HostnameEntry>
}
impl Default for HostConfig
{
fn default() -> Self
{
return
Self
{
hostnames: Default::default(),
};
}
}
impl HostConfig
{
pub
fn is_empty(&self) -> bool
{
return self.hostnames.is_empty();
}
pub
fn search_by_ip(&self, ip: &IpAddr) -> Option<&HostnameEntry>
{
return self.hostnames.get(ip);
}
pub
fn search_by_fqdn(&self, qtype: &QType, name: &str) -> Option<&HostnameEntry>
{
for host in self.hostnames.iter()
{
for fqdn in host.hostnames.iter()
{
if name == fqdn.as_str() && qtype.ipaddr_match(&host.ip)
{
return Some(host);
}
}
}
return None;
}
pub
fn parse_host_file_internal(file_content: String, f: &mut Writer) -> CDnsResult<Self>
{
let mut tk = ConfTokenizer::from_str(&file_content)?;
let mut he_list: HashSet<HostnameEntry> = HashSet::new();
loop
{
let field_ip = tk.read_next()?;
if field_ip.is_none() == true
{
break;
}
let ip: IpAddr =
match field_ip.unwrap().parse()
{
Ok(r) => r,
Err(_e) =>
{
tk.skip_upto_eol();
continue;
}
};
let hostnames = tk.read_upto_eol()?;
if hostnames.len() > 0
{
let he = HostnameEntry{ ip: ip, hostnames: hostnames };
he_list.insert(he);
}
else
{
writer_error!(f, "in file: '{}' IP is not defined with domain name: '{}'\n", HOST_CFG_PATH, ip);
}
}
if he_list.len() == 0
{
writer_error!(f, "file: '{}' file is empty or damaged!\n", HOST_CFG_PATH);
}
return Ok(
Self
{
hostnames: he_list
}
);
}
}
#[test]
fn test_parse_host_file_0()
{
let hosts1: Vec<&'static str> = vec!["debian-laptop"];
let hosts2: Vec<&'static str> = vec!["localhost", "ip6-localhost", "ip6-loopback"];
let hosts3: Vec<&'static str> = vec!["ip6-allnodes"];
let hosts4: Vec<&'static str> = vec!["ip6-allrouters"];
let ip1: IpAddr = "127.0.1.1".parse().unwrap();
let ip2: IpAddr = "::1".parse().unwrap();
let ip3: IpAddr = "ff02::1".parse().unwrap();
let ip4: IpAddr = "ff02::2".parse().unwrap();
let ip_list =
vec![
(ip1, hosts1),
(ip2, hosts2),
(ip3, hosts3),
(ip4, hosts4)
];
let test =
"127.0. 0.1 localhost
127.0.1.1 debian-laptop
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters".to_string();
let mut writer = Writer::new();
let p = HostConfig::parse_host_file_internal(test, &mut writer);
assert_eq!(p.is_ok(), true, "{}", p.err().unwrap());
let p = p.unwrap();
for (ip, host) in ip_list
{
let res = p.hostnames.get(&ip);
assert_eq!(res.is_some(), true);
let res = res.unwrap();
assert_eq!(res.hostnames, host);
}
return;
}
#[test]
fn test_parse_host_file()
{
let ip0:IpAddr = "127.0.0.1".parse().unwrap();
let ip1:IpAddr = "127.0.1.1".parse().unwrap();
let ip2:IpAddr = "::1".parse().unwrap();
let ip3:IpAddr = "ff02::1".parse().unwrap();
let ip4:IpAddr = "ff02::2".parse().unwrap();
let hosts0: Vec<&'static str> = vec!["localhost"];
let hosts1: Vec<&'static str> = vec!["debian-laptop"];
let hosts2: Vec<&'static str> = vec!["localhost", "ip6-localhost", "ip6-loopback"];
let hosts3: Vec<&'static str> = vec!["ip6-allnodes"];
let hosts4: Vec<&'static str> = vec!["ip6-allrouters"];
let ip_list =
vec![
(ip0, hosts0),
(ip1, hosts1),
(ip2, hosts2),
(ip3, hosts3),
(ip4, hosts4)
];
let test =
"127.0.0.1 localhost
127.0.1.1 debian-laptop
# The following lines are desirable for IPv6 capable hosts
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters".to_string();
let mut writer = Writer::new();
let p = HostConfig::parse_host_file_internal(test, &mut writer);
assert_eq!(p.is_ok(), true, "{}", p.err().unwrap());
let p = p.unwrap();
for (ip, host) in ip_list
{
let res = p.hostnames.get(&ip);
assert_eq!(res.is_some(), true);
let res = res.unwrap();
assert_eq!(res.hostnames, host);
}
}
#[test]
fn test_parse_host_file_2()
{
let ip0:IpAddr = "127.0.0.1".parse().unwrap();
let ip1:IpAddr = "127.0.1.1".parse().unwrap();
let ip2:IpAddr = "::1".parse().unwrap();
let ip3:IpAddr = "ff02::1".parse().unwrap();
let ip4:IpAddr = "ff02::2".parse().unwrap();
let hosts0: Vec<&'static str> = vec!["localhost", "localdomain", "domain.local"];
let hosts1: Vec<&'static str> = vec!["debian-laptop", "test123.domain.tld"];
let hosts2: Vec<&'static str> = vec!["localhost", "ip6-localhost", "ip6-loopback"];
let hosts3: Vec<&'static str> = vec!["ip6-allnodes"];
let hosts4: Vec<&'static str> = vec!["ip6-allrouters"];
let ip_list =
vec![
(ip0, hosts0),
(ip1, hosts1),
(ip2, hosts2),
(ip3, hosts3),
(ip4, hosts4)
];
let test =
"127.0.0.1 localhost localdomain domain.local
127.0.1.1 debian-laptop test123.domain.tld
# The following lines are desirable for IPv6 capable hosts
#
#
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
".to_string();
let mut writer = Writer::new();
let p = HostConfig::parse_host_file_internal(test, &mut writer);
assert_eq!(p.is_ok(), true, "{}", p.err().unwrap());
let p = p.unwrap();
for (ip, host) in ip_list
{
let res = p.hostnames.get(&ip);
assert_eq!(res.is_some(), true);
let res = res.unwrap();
assert_eq!(res.hostnames, host);
}
}