use std::hash::Hash;
use std::net::{IpAddr, SocketAddr};
use std::str::FromStr;
use std::sync::atomic::{AtomicUsize, Ordering};
use bitflags::bitflags;
use crate::{internal_error, internal_error_map, error::*, writer_error};
use crate::portable::*;
use crate::tokenizer::*;
use super::common::{IPV4_BIND_ALL, IPV6_BIND_ALL};
lazy_static!{
pub(super) static ref ROUND_ROBIN_CNT: AtomicUsize = AtomicUsize::new(0);
}
pub struct RoundRobinIterator<'iterator>
{
end_index: usize,
current_index: usize,
slice: &'iterator [ResolveConfEntry]
}
impl<'iterator> RoundRobinIterator<'iterator>
{
pub
fn new(start_index: usize, slice: &'iterator [ResolveConfEntry]) -> RoundRobinIterator<'iterator>
{
return
RoundRobinIterator
{
end_index: start_index + slice.len(),
current_index: start_index,
slice: slice
};
}
pub
fn len(&self) -> usize
{
return self.slice.len();
}
}
impl<'iterator> Iterator for RoundRobinIterator<'iterator>
{
type Item = &'iterator ResolveConfEntry;
fn next(&mut self) -> Option<Self::Item>
{
let temp_index = self.current_index;
self.current_index += 1;
if temp_index == self.end_index
{
return None;
}
let real_index = temp_index % self.slice.len();
return self.slice.get(real_index);
}
}
#[derive(Clone, Debug)]
pub struct ResolveConfEntry
{
ip: IpAddr,
adapter_ip: SocketAddr,
}
impl ResolveConfEntry
{
pub
fn get_resolver_ip(&self) -> &IpAddr
{
return &self.ip;
}
pub
fn get_adapter_ip(&self) -> &SocketAddr
{
return &self.adapter_ip;
}
}
bitflags! {
#[derive(Clone, Debug, PartialEq, Eq, Copy)]
pub struct ResolveConfigLookup: u32
{
const FILE_BIND = 0x10;
const BIND_FILE = 0x20;
const FILE = 0x01;
const BIND = 0x02;
}
}
impl ResolveConfigLookup
{
pub
fn is_file(&self) -> bool
{
return self.contains(Self::FILE);
}
pub
fn is_bind(&self) -> bool
{
return self.contains(Self::BIND);
}
pub
fn is_file_first(&self) -> bool
{
return self.contains(Self::FILE_BIND);
}
}
impl Default for ResolveConfigLookup
{
fn default() -> Self
{
return Self::FILE_BIND | Self::FILE | Self::BIND;
}
}
const LOOKUP_FILE: &'static str = "file";
const LOOKUP_BIND: &'static str = "bind";
impl TryFrom<&[String]> for ResolveConfigLookup
{
type Error = CDnsError;
fn try_from(value: &[String]) -> Result<Self, Self::Error>
{
if value.is_empty() == true
{
internal_error!(CDnsErrorType::ConfigError, "'lookup' is empty");
}
else if value.len() > 2
{
internal_error!(CDnsErrorType::ConfigError, "'lookup' is set with too many paramenters: '{:?}'", value);
}
let first =
if value[0] == LOOKUP_FILE
{
Self::FILE
}
else if value[0] == LOOKUP_BIND
{
Self::BIND
}
else
{
internal_error!(CDnsErrorType::ConfigError, "'lookup' is set with unknown parameter: '{}'", value[0]);
};
let second: Self =
if value.len() == 2
{
if value[1] == LOOKUP_FILE
{
if first.contains(Self::FILE) == true
{
internal_error!(CDnsErrorType::ConfigError, "'lookup' is set with duplicate parameter: '{}'", value[1]);
}
Self::BIND_FILE | Self::FILE
}
else if value[1] == LOOKUP_BIND
{
if first.contains(Self::BIND) == true
{
internal_error!(CDnsErrorType::ConfigError, "'lookup' is set with duplicate parameter: '{}'", value[1]);
}
Self::FILE_BIND | Self::BIND
}
else
{
internal_error!(CDnsErrorType::ConfigError, "'lookup' is set with unknown parameter: '{}'", value[0]);
}
}
else
{
Self::empty()
};
return Ok(Self::from(first | second));
}
}
bitflags! {
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub struct ResolveConfigFamily: u32
{
const INET4_INET6 = 0x10;
const INET6_INET4 = 0x20;
const INET4 = 0x01;
const INET6 = 0x02;
}
}
impl ResolveConfigFamily
{
pub
fn is_inet4(&self) -> bool
{
return self.contains(Self::INET4);
}
pub
fn is_inet6(&self) -> bool
{
return self.contains(Self::INET6);
}
pub
fn is_inet4_first(&self) -> bool
{
return self.contains(Self::INET4_INET6);
}
fn inverted_default() -> Self
{
return Self::INET6_INET4 | Self::INET4 | Self::INET6;
}
}
impl Default for ResolveConfigFamily
{
fn default() -> Self
{
return Self::INET4_INET6 | Self::INET4 | Self::INET6;
}
}
const FAMILY_INET4: &'static str = "inet4";
const FAMILY_INET6: &'static str = "inet6";
impl TryFrom<&[String]> for ResolveConfigFamily
{
type Error = CDnsError;
fn try_from(value: &[String]) -> Result<Self, Self::Error>
{
if value.is_empty() == true
{
internal_error!(CDnsErrorType::ConfigError, "'family' is empty");
}
else if value.len() > 2
{
internal_error!(CDnsErrorType::ConfigError, "'family' is set with too many paramenters: '{:?}'", value);
}
let mut family =
if value[0] == FAMILY_INET4
{
Self::INET4_INET6 | Self::INET4
}
else if value[0] == FAMILY_INET6
{
Self::INET6_INET4 | Self::INET6
}
else
{
internal_error!(CDnsErrorType::ConfigError, "'family' is set with unknown parameter: '{}'", value[0]);
};
if value.len() == 2
{
if value[1] == FAMILY_INET4
{
if family.contains(Self::INET4) == true
{
internal_error!(CDnsErrorType::ConfigError, "'family' is set with duplicate parameter: '{}'", value[1]);
}
family |= Self::INET4
}
else if value[1] == FAMILY_INET6
{
if family.contains(Self::INET6) == true
{
internal_error!(CDnsErrorType::ConfigError, "'family' is set with duplicate parameter: '{}'", value[1]);
}
family |= Self::INET6
}
else
{
internal_error!(CDnsErrorType::ConfigError, "'family' is set with unknown parameter: '{}'", value[0]);
}
}
return Ok(family);
}
}
bitflags! {
#[derive(Debug, Copy, Clone)]
pub struct OptionFlags: u16
{
const OPT_DEBUG = 0x0001;
const OPT_ROTATE = 0x0002;
const OPT_NO_CHECK_NAMES = 0x0004;
const OPT_INET6 = 0x0008;
const OPT_SINGLE_REQUEST = 0x0010;
const OPT_SINGLE_REQUEST_REOPEN = 0x0020;
const OPT_NO_RELOAD = 0x0040;
const OPT_TRUST_AD = 0x0080;
const OPT_USE_VC = 0x0100;
}
}
impl Default for OptionFlags
{
fn default() -> Self
{
return Self::empty();
}
}
impl OptionFlags
{
pub
fn is_debug(&self) -> bool
{
return self.contains(Self::OPT_DEBUG);
}
pub
fn is_rotate(&self) -> bool
{
return self.contains(Self::OPT_ROTATE);
}
pub
fn is_no_check_names(&self) -> bool
{
return self.contains(Self::OPT_NO_CHECK_NAMES);
}
pub
fn is_no_parallel(&self) -> bool
{
return self.contains(Self::OPT_SINGLE_REQUEST);
}
pub
fn is_reopen_socket(&self) -> bool
{
return self.contains(Self::OPT_SINGLE_REQUEST_REOPEN);
}
pub
fn is_force_tcp(&self) -> bool
{
return self.contains(Self::OPT_USE_VC);
}
}
#[derive(Clone, Debug, Default)]
pub struct ResolveConfig
{
pub nameservers: Vec<ResolveConfEntry>,
pub lookup: ResolveConfigLookup,
pub family: ResolveConfigFamily,
pub search_list: Vec<String>,
pub domain: Option<String>,
pub option_flags: OptionFlags,
pub ndots: usize,
pub timeout: u32,
pub attempts: usize,
}
impl ResolveConfig
{
pub
fn get_resolvers_iter(&self) -> RoundRobinIterator
{
if self.option_flags.is_rotate() == true
{
let start_index = ROUND_ROBIN_CNT.fetch_add(1, Ordering::SeqCst);
return RoundRobinIterator::new(start_index, self.nameservers.as_slice());
}
else
{
return RoundRobinIterator::new(0, self.nameservers.as_slice());
};
}
unsafe
fn split_dns_ifr(field_data: &str, ifr: Option<&IfInfo>) -> CDnsResult<(IpAddr, SocketAddr)>
{
if let Some((ip, intf)) = field_data.split_once("%")
{
let ip_addr: IpAddr =
ip.parse().map_err(|e|
internal_error_map!(CDnsErrorType::InternalError, "parse IP: '{}' failed: '{}'", ip, e)
)?;
if ifr.is_none() == true
{
internal_error!(
CDnsErrorType::InternalError,
"can not add: '{}' to list because interface data is not available!",
field_data
);
}
let ifrr = ifr.unwrap();
match ifrr.get_ifr_ip(intf, &ip_addr)?
{
Some(ip_ifr) => return Ok( (ip_addr, SocketAddr::from((ip_ifr, 0))) ),
None => internal_error!(CDnsErrorType::InternalError, "interface: '{}' does not exist", intf),
}
}
else
{
let ip_addr: IpAddr =
match field_data.parse()
{
Ok(r) => r,
Err(e) =>
internal_error!(CDnsErrorType::InternalError, "parse IP: '{}' failed: '{}'", field_data, e)
};
let ip_ifr =
match ip_addr
{
IpAddr::V4(_) =>
SocketAddr::from((IPV4_BIND_ALL, 0)),
IpAddr::V6(_) =>
SocketAddr::from((IPV6_BIND_ALL, 0)),
};
return Ok((ip_addr, ip_ifr))
}
}
#[inline]
fn set_option(
optname: &'static str,
right: Option<&str>,
option_flags: &mut OptionFlags,
opt_flag: OptionFlags
) -> CDnsResult<()>
{
if right.is_some() == true
{
internal_error!(CDnsErrorType::ConfigError,
"in /etc/resolv.conf - 'option' '{}' is defined \
with parameter: '{}', but it does not have parameters", optname, right.unwrap());
}
option_flags.set(opt_flag, true);
return Ok(());
}
#[inline]
fn read_parameter<P: FromStr>(optname: &'static str, right: Option<&str>) -> CDnsResult<P>
{
if right.is_none() == true
{
internal_error!(CDnsErrorType::ConfigError,
"in /etc/resolv.conf - 'option' '{}' is defined without parameter", optname);
}
else
{
return
right.unwrap()
.parse::<P>()
.map_err(|_|
internal_error_map!(
CDnsErrorType::ConfigError,
"in /etc/resolv.conf - 'option' '{}' is defined with invalid \
parameter: '{}'", optname, right.unwrap()
)
);
}
}
pub(crate)
fn parser_resolv_internal(file_content: &str, f: &mut Writer) -> CDnsResult<Self>
{
let mut tk = ConfTokenizer::from_str(&file_content)?;
let mut dns_list: Vec<ResolveConfEntry> = Vec::new();
let mut lookup_list: ResolveConfigLookup = ResolveConfigLookup::default();
let mut family_list: ResolveConfigFamily = ResolveConfigFamily::default();
let mut search_list: Vec<String> = Vec::new();
let mut domain: Option<String> = None;
let mut option_flags: OptionFlags = OptionFlags::empty();
let mut ndots: usize = 1;
let mut timeout: u32 = 5;
let mut attempts: usize = 2;
let if_info: Option<IfInfo> =
unsafe
{
match IfInfo::get_interfaces_info()
{
Ok(r) => Some(r),
Err(e) =>
{
writer_error!(f, "{}", e);
None
}
}
};
loop
{
let field_name = tk.read_next()?;
if field_name.is_none() == true
{
break;
}
let field_name = field_name.unwrap();
if field_name == "nameserver"
{
let field_data = tk.read_next()?;
if field_data.is_none() == true
{
writer_error!(f, "unexpected EOF near nameserver");
break;
}
let field_data = field_data.unwrap();
let (dns_ip, ifr_ip) =
unsafe
{
match Self::split_dns_ifr(field_data, if_info.as_ref())
{
Ok((d_ip, i_ip)) => (d_ip, i_ip),
Err(e) =>
{
writer_error!(f, "{}", e);
continue;
}
}
};
dns_list.push(
ResolveConfEntry
{
ip: dns_ip,
adapter_ip: ifr_ip,
}
);
}
else if field_name == "lookup" {
let lookups = tk.read_upto_eol()?;
if lookups.len() == 0
{
writer_error!(f, "in /etc/resolv.conf - 'lookup' is set but no args provided, setting defaults file, bind");
lookup_list = ResolveConfigLookup::default();
continue;
}
lookup_list =
match ResolveConfigLookup::try_from(lookups.as_slice())
{
Ok(r) => r,
Err(e) =>
{
writer_error!(f, "in /etc/resolv.conf - {}", e);
continue;
}
};
}
else if field_name == "family" {
let families = tk.read_upto_eol()?;
if families.len() == 0
{
writer_error!(f, "in /etc/resolv.conf - 'family' is set but no args provided, setting defaults inet4, inet6");
family_list = ResolveConfigFamily::default();
continue;
}
family_list =
match ResolveConfigFamily::try_from(families.as_slice())
{
Ok(r) => r,
Err(e) =>
{
writer_error!(f, "in /etc/resolv.conf - {}", e);
continue;
}
};
}
else if field_name == "search"
{
let searches = tk.read_upto_eol()?;
if searches.len() == 0
{
writer_error!(f, "in /etc/resolv.conf - 'search' is defined but empty");
}
else
{
search_list.extend(searches);
}
}
else if field_name == "domain"
{
let mut domains = tk.read_upto_eol()?;
if domain.is_some() == true
{
writer_error!(f, "in /etc/resolv.conf - 'domain' is defined more than once: {:?} {:?}", domain, domains);
}
if domains.len() > 1
{
writer_error!(f, "in /etc/resolv.conf - 'domain' is defined with more than 1 address: {:?}", domains);
}
else if domains.len() == 0
{
writer_error!(f, "in /etc/resolv.conf - 'domain' is defined but empty");
}
else
{
domain = Some(domains.pop().unwrap());
}
}
else if field_name == "options"
{
let options_vec = tk.read_upto_eol()?;
if options_vec.is_empty() == true
{
writer_error!(f, "in /etc/resolv.conf - 'option' is empty!");
continue;
}
for option in options_vec
{
let (left, right) =
match option.split_once(":")
{
Some((l, r)) => (l, Some(r)),
None => (option.as_str(), None),
};
match left
{
"use-vc" | "usevc" | "tcp" => {
match Self::set_option("usevc", right, &mut option_flags, OptionFlags::OPT_USE_VC)
{
Err(e) => { writer_error!(f, "{}", e); },
Ok(_) => {}
}
},
"debug" =>
{
match Self::set_option("debug", right, &mut option_flags, OptionFlags::OPT_DEBUG)
{
Err(e) => { writer_error!(f, "{}", e); },
Ok(_) => {}
}
},
"ndots" =>
{
match Self::read_parameter("ndots", right)
{
Ok(val) =>
{
ndots = if val > 15 { 15 } else { val };
},
Err(e) =>
{
writer_error!(f, "{}", e);
}
}
},
"timeout" =>
{
match Self::read_parameter::<u32>("timeout", right)
{
Ok(val) =>
{
timeout = if val > 30 { 30 } else { val };
},
Err(e) =>
{
writer_error!(f, "{}", e);
}
}
},
"attempts" =>
{
match Self::read_parameter("attempts", right)
{
Ok(val) =>
{
attempts = if val > 5 { 5 } else { val };
},
Err(e) =>
{
writer_error!(f, "{}", e);
}
}
},
"rotate" =>
{
match Self::set_option("rotate", right, &mut option_flags, OptionFlags::OPT_ROTATE)
{
Err(e) => { writer_error!(f, "{}", e); },
Ok(_) => {}
}
},
"no-check-names" =>
{
match Self::set_option("no-check-names", right, &mut option_flags, OptionFlags::OPT_NO_CHECK_NAMES)
{
Err(e) => { writer_error!(f, "{}", e); },
Ok(_) => {}
}
},
"no-reload" =>
{
match Self::set_option("no-reload", right, &mut option_flags, OptionFlags::OPT_NO_RELOAD)
{
Err(e) => { writer_error!(f, "{}", e); },
Ok(_) => {}
}
},
"inet6" =>
{
match Self::set_option("inet6", right, &mut option_flags, OptionFlags::OPT_INET6)
{
Err(e) => { writer_error!(f, "{}", e); },
Ok(_) => {}
}
},
"edns0" =>
{
},
"single-request" =>
{
match Self::set_option("single-request", right, &mut option_flags, OptionFlags::OPT_SINGLE_REQUEST)
{
Err(e) => { writer_error!(f, "{}", e); },
Ok(_) => {}
}
},
"single-request-reopen" =>
{
match Self::set_option("single-request-reopen", right, &mut option_flags, OptionFlags::OPT_SINGLE_REQUEST_REOPEN)
{
Err(e) => { writer_error!(f, "{}", e); },
Ok(_) => {}
}
},
"trust-ad" =>
{
match Self::set_option("trust-ad", right, &mut option_flags, OptionFlags::OPT_TRUST_AD)
{
Err(e) => { writer_error!(f, "{}", e); },
Ok(_) => {}
}
},
_ =>
{
writer_error!(f, "in /etc/resolv.conf - 'option' '{}' is unknown \
with parameter: '{:?}'", left, right);
},
}
}
}
else
{
tk.skip_upto_eol();
}
}
if option_flags.contains(OptionFlags::OPT_INET6) == true
{
family_list = ResolveConfigFamily::inverted_default();
}
return Ok(
Self
{
nameservers: dns_list,
lookup: lookup_list,
family: family_list,
search_list: search_list,
domain: domain,
option_flags: option_flags,
ndots: ndots,
timeout: timeout,
attempts: attempts,
}
);
}
}
#[test]
fn test_parser_resolv_internal_0()
{
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
use std::str::FromStr;
let ip1: IpAddr = "192.168.2.1".parse().unwrap();
let ip2: IpAddr = "127.0.0.1".parse().unwrap();
let ip3: IpAddr = "8.8.8.8".parse().unwrap();
let ip4: IpAddr = "127.0.0.1".parse().unwrap();
let ip4_if: SocketAddr = SocketAddr::from((Ipv4Addr::from_str("127.0.0.1").unwrap(), 0));
let ip5: IpAddr = "fe80::1".parse().unwrap();
let ip5_if: SocketAddr = SocketAddr::from((Ipv6Addr::from_str("::1").unwrap(), 0));
let test =
"nameserver 192.168.2.1\n\
nameserver 127.0.0.1\n\
lookup bind\n\
nameserver 8.8.8.8\n
nameserver 127.0.0.1%lo\n
nameserver fe80::1%lo\n";
let mut writer = Writer::new();
let res = ResolveConfig::parser_resolv_internal(test, &mut writer);
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let res = res.unwrap();
assert_eq!(res.lookup, ResolveConfigLookup::BIND);
assert_eq!(res.family, ResolveConfigFamily::INET4_INET6 | ResolveConfigFamily::INET6 | ResolveConfigFamily::INET4);
assert_eq!(res.nameservers[0].ip, ip1);
assert_eq!(res.nameservers[1].ip, ip2);
assert_eq!(res.nameservers[2].ip, ip3);
assert_eq!(res.nameservers[3].ip, ip4);
assert_eq!(res.nameservers[3].adapter_ip, ip4_if);
assert_eq!(res.nameservers[4].ip, ip5);
assert_eq!(res.nameservers[4].adapter_ip, ip5_if);
}
#[test]
fn test_parser_resolv_internal()
{
let ip1: IpAddr = "192.168.2.1".parse().unwrap();
let ip2: IpAddr = "127.0.0.1".parse().unwrap();
let ip3: IpAddr = "8.8.8.8".parse().unwrap();
let test =
"nameserver 192.168.2.1\n\
nameserver 127.0.0.1\n\
lookup bind file\n\
nameserver 8.8.8.8";
let mut writer = Writer::new();
let res = ResolveConfig::parser_resolv_internal(test, &mut writer);
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let res = res.unwrap();
assert_eq!(res.lookup, ResolveConfigLookup::BIND | ResolveConfigLookup::FILE | ResolveConfigLookup::BIND_FILE);
assert_eq!(res.family, ResolveConfigFamily::INET4_INET6 | ResolveConfigFamily::INET6 | ResolveConfigFamily::INET4);
assert_eq!(res.nameservers[0].ip, ip1);
assert_eq!(res.nameservers[1].ip, ip2);
assert_eq!(res.nameservers[2].ip, ip3);
}
#[test]
fn test_parser_resolv_internal2()
{
let ip1: IpAddr = "192.168.2.1".parse().unwrap();
let ip2: IpAddr = "127.0.0.1".parse().unwrap();
let ip3: IpAddr = "8.8.8.8".parse().unwrap();
let test =
"# test 12345\n\
# nameserver 192.168.3.1
nameserver 192.168.2.1 \n\
nameserver 127.0.0.1 \n\
lookup file\n\
family inet4\n\
nameserver 8.8.8.8\n";
let mut writer = Writer::new();
let res = ResolveConfig::parser_resolv_internal(test, &mut writer);
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let res = res.unwrap();
assert_eq!(res.lookup, ResolveConfigLookup::FILE);
assert_eq!(res.family, ResolveConfigFamily::INET4);
assert_eq!(res.nameservers.len(), 3);
assert_eq!(res.nameservers[0].ip, ip1);
assert_eq!(res.nameservers[1].ip, ip2);
assert_eq!(res.nameservers[2].ip, ip3);
}
#[test]
fn test_parser_resolv_internal3()
{
let ip1: IpAddr = "192.168.2.1".parse().unwrap();
let ip2: IpAddr = "127.0.0.1".parse().unwrap();
let ip3: IpAddr = "8.8.8.8".parse().unwrap();
let test =
"# test 12345\n\
# nameserver 192.168.3.1
nameserver 192.168.2.1 \n\
nameserver 127.0.0.1 \n\
lookup file\n\
family inet4\n\
nameserver 8.8.8.8\n
options attempts:1 timeout:3 debug use-vc single-request-reopen\n
search localdomain";
let mut writer = Writer::new();
let res = ResolveConfig::parser_resolv_internal(test, &mut writer);
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let res = res.unwrap();
assert_eq!(res.lookup, ResolveConfigLookup::FILE);
assert_eq!(res.family, ResolveConfigFamily::INET4);
assert_eq!(res.nameservers.len(), 3);
assert_eq!(res.nameservers[0].ip, ip1);
assert_eq!(res.nameservers[1].ip, ip2);
assert_eq!(res.nameservers[2].ip, ip3);
assert_eq!(res.search_list[0], "localdomain");
assert_eq!(res.timeout, 3);
assert_eq!(res.attempts, 1);
assert_eq!(res.option_flags.contains(OptionFlags::OPT_DEBUG), true);
assert_eq!(res.option_flags.contains(OptionFlags::OPT_USE_VC), true);
assert_eq!(res.option_flags.contains(OptionFlags::OPT_SINGLE_REQUEST_REOPEN), true);
}
#[test]
fn test_parser_resolv_internal4()
{
let ip1: IpAddr = "192.168.2.1".parse().unwrap();
let ip2: IpAddr = "127.0.0.1".parse().unwrap();
let ip3: IpAddr = "8.8.8.8".parse().unwrap();
let test =
"\
# test 12345\n\
# nameserver 192.168.3.1\n\
nameserver 192.168.2.1 \n\
nameserver 127.0.0.1 \n\
lookup file\n\
family inet4 \n\
nameserver 8.8.8.8\n\
options attempts:1 timeout:3 debug use-vc single-request-reopen\n\
search localdomain\n\
";
let mut writer = Writer::new();
let res = ResolveConfig::parser_resolv_internal(test, &mut writer);
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let res = res.unwrap();
assert_eq!(res.lookup, ResolveConfigLookup::FILE);
assert_eq!(res.family, ResolveConfigFamily::INET4);
assert_eq!(res.nameservers.len(), 3);
assert_eq!(res.nameservers[0].ip, ip1);
assert_eq!(res.nameservers[1].ip, ip2);
assert_eq!(res.nameservers[2].ip, ip3);
assert_eq!(res.search_list[0], "localdomain");
assert_eq!(res.timeout, 3);
assert_eq!(res.attempts, 1);
assert_eq!(res.option_flags.contains(OptionFlags::OPT_DEBUG), true);
assert_eq!(res.option_flags.contains(OptionFlags::OPT_USE_VC), true);
assert_eq!(res.option_flags.contains(OptionFlags::OPT_SINGLE_REQUEST_REOPEN), true);
}
#[test]
fn test_roundrobin_iter_1()
{
use std::time::Instant;
fn check_order(res: &ResolveConfig, ipl: &[IpAddr], indx: usize)
{
let mut i = indx;
let assert_i = (indx + ipl.len()) % ipl.len();
let now = Instant::now();
let itr = res.get_resolvers_iter();
assert_eq!(itr.len(), ipl.len());
for n in itr
{
assert_eq!(n.ip, ipl[i]);
i += 1;
i %= ipl.len();
}
assert_eq!(i, assert_i);
let elapsed = now.elapsed();
println!("Iter Elapsed: {:.2?}", elapsed);
}
let ip1: IpAddr = "192.168.2.1".parse().unwrap();
let ip_list: Vec<IpAddr> = vec![ip1];
let test =
"\
# test 12345\n\
# nameserver 192.168.3.1\n\
nameserver 192.168.2.1 \n\
#nameserver 127.0.0.1 \n\
lookup file\n\
family inet4 \n\
#nameserver 8.8.8.8\n\
options attempts:1 rotate timeout:3 debug use-vc single-request-reopen\n\
search localdomain\n\
";
let now = Instant::now();
let mut writer = Writer::new();
let res = ResolveConfig::parser_resolv_internal(test, &mut writer);
let elapsed = now.elapsed();
println!("parser_resolv_internal elapsed: {:.2?}", elapsed);
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let res = res.unwrap();
check_order(&res, &ip_list, 0);
check_order(&res, &ip_list, 0);
check_order(&res, &ip_list, 0);
check_order(&res, &ip_list, 0);
check_order(&res, &ip_list, 0);
check_order(&res, &ip_list, 0);
check_order(&res, &ip_list, 0);
check_order(&res, &ip_list, 0);
check_order(&res, &ip_list, 0);
}
#[test]
fn test_roundrobin_iter()
{
use std::time::Instant;
fn check_order(res: &ResolveConfig, ipl: &[IpAddr], indx: usize)
{
let mut i = indx;
let assert_i = (indx + ipl.len()) % ipl.len();
let now = Instant::now();
let itr = res.get_resolvers_iter();
assert_eq!(itr.len(), ipl.len());
for n in itr
{
assert_eq!(n.ip, ipl[i]);
i += 1;
i %= ipl.len();
}
assert_eq!(i, assert_i);
let elapsed = now.elapsed();
println!("Iter Elapsed: {:.2?}", elapsed);
}
let ip1: IpAddr = "192.168.2.1".parse().unwrap();
let ip2: IpAddr = "127.0.0.1".parse().unwrap();
let ip3: IpAddr = "8.8.8.8".parse().unwrap();
let ip_list: Vec<IpAddr> = vec![ip1, ip2, ip3];
let test =
"\
# test 12345\n\
# nameserver 192.168.3.1\n\
nameserver 192.168.2.1 \n\
nameserver 127.0.0.1 \n\
lookup file\n\
family inet4 \n\
nameserver 8.8.8.8\n\
options attempts:1 rotate timeout:3 debug use-vc single-request-reopen\n\
search localdomain\n\
";
let now = Instant::now();
let mut writer = Writer::new();
let res = ResolveConfig::parser_resolv_internal(test, &mut writer);
let elapsed = now.elapsed();
println!("parser_resolv_internal elapsed: {:.2?}", elapsed);
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let res = res.unwrap();
check_order(&res, &ip_list, 0);
check_order(&res, &ip_list, 1);
check_order(&res, &ip_list, 2);
check_order(&res, &ip_list, 0);
check_order(&res, &ip_list, 1);
check_order(&res, &ip_list, 2);
check_order(&res, &ip_list, 0);
check_order(&res, &ip_list, 1);
check_order(&res, &ip_list, 2);
}
#[test]
fn test_direct_iter()
{
use std::time::Instant;
let ip1: IpAddr = "192.168.2.1".parse().unwrap();
let ip2: IpAddr = "192.168.2.2".parse().unwrap();
let test =
"\
# test 12345\n\
# nameserver 192.168.3.1\n\
nameserver 192.168.2.1 \n\
nameserver 192.168.2.2 \n\
lookup file\n\
family inet4 \n\
options attempts:1 timeout:3 debug use-vc single-request-reopen\n\
search localdomain\n\
";
let now = Instant::now();
let mut writer = Writer::new();
let res = ResolveConfig::parser_resolv_internal(test, &mut writer);
let elapsed = now.elapsed();
println!("Elapsed: {:.2?}", elapsed);
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let res = res.unwrap();
let mut i: usize = 0;
let now = Instant::now();
for n in res.get_resolvers_iter()
{
match i
{
0 => { assert_eq!(n.ip, ip1); i += 1; },
1 => { assert_eq!(n.ip, ip2); i += 1; },
_ => panic!("!")
}
}
let elapsed = now.elapsed();
println!("Iter Elapsed: {:.2?}", elapsed);
assert_eq!(i, 2);
i = 0;
for n in res.get_resolvers_iter()
{
match i
{
0 => { assert_eq!(n.ip, ip1); i += 1; },
1 => { assert_eq!(n.ip, ip2); i += 1; },
_ => panic!("!")
}
}
assert_eq!(i, 2);
}