use std::fmt;
use std::hash::Hash;
use std::net::{IpAddr, SocketAddr};
use std::str::FromStr;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::sync::Arc;
use bitflags::bitflags;
use crate::common::{bind_all, DEF_HTTPS_PORT, DEF_TLS_PORT};
use crate::{error::*, internal_error, internal_error_map, write_error};
use crate::portable::*;
use crate::tokenizer::*;
static ROUND_ROBIN_CNT: AtomicUsize = AtomicUsize::new(0);
pub struct RoundRobinIterator<'iterator, I>
{
end_index: usize,
current_index: usize,
slice: &'iterator [I]
}
impl<'iterator, I> RoundRobinIterator<'iterator, I>
{
pub
fn new(start_index: usize, slice: &'iterator [I]) -> RoundRobinIterator<'iterator, I>
{
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, I> Iterator for RoundRobinIterator<'iterator, I>
{
type Item = &'iterator I;
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, Copy, Debug, PartialEq, Eq)]
pub enum ConfigEntryTls
{
None, Tls, Https
}
impl fmt::Display for ConfigEntryTls
{
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result
{
match self
{
Self::None => write!(f, "unencrypted"),
Self::Tls => write!(f, "TLS"),
Self::Https => write!(f, "HTTPS"),
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ResolveConfEntry
{
ip: SocketAddr,
tls_domain: Option<String>,
https_req_path: Option<String>,
adapter_ip: SocketAddr,
tls_type: ConfigEntryTls,
}
impl ResolveConfEntry
{
pub
fn new(ip: SocketAddr, tls_url: Option<String>, adapter_ip: SocketAddr) -> CDnsResult<Self>
{
let (tls_type, https_domain, https_req_path) =
if let Some(tlsd) = tls_url.as_ref()
{
if let Some(domain_path_https) = tlsd.strip_prefix("https://")
{
let Some ((domain, path)) = domain_path_https.split_once("/")
else
{
internal_error!(CDnsErrorType::ConfigError,
"in /etc/resolv.conf - invalid TLS/HTTPS URL '{}'",
domain_path_https
)
};
(ConfigEntryTls::Https, Some(domain.to_string()), Some(path.to_string()))
}
else
{
let domain =
tlsd
.split_once("://")
.map_or(tlsd.as_str(), |(_, d)| d);
let domain =
domain
.split_once("/")
.map_or(domain, |(d, _)| d);
(ConfigEntryTls::Tls, Some(domain.to_string()), None)
}
}
else
{
if ip.port() == DEF_HTTPS_PORT
{
(ConfigEntryTls::Https, None, None)
}
else if ip.port() == DEF_TLS_PORT
{
(ConfigEntryTls::Tls, None, None)
}
else
{
(ConfigEntryTls::None, None, None)
}
};
return Ok(Self {ip, tls_domain: https_domain, https_req_path: https_req_path, adapter_ip, tls_type});
}
pub
fn get_resolver_ip(&self) -> IpAddr
{
return self.ip.ip();
}
pub
fn get_resolver_sa(&self) -> &SocketAddr
{
return &self.ip;
}
pub
fn get_tls_domain(&self) -> Option<&String>
{
return self.tls_domain.as_ref();
}
pub
fn get_adapter_ip(&self) -> &SocketAddr
{
return &self.adapter_ip;
}
pub
fn get_tls_type(&self) -> ConfigEntryTls
{
return self.tls_type;
}
pub
fn get_tls_path(&self) -> Option<&String>
{
return self.https_req_path.as_ref();
}
}
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, PartialEq, Eq)]
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, PartialEq, Eq)]
pub struct ResolveConfig
{
pub nameservers: Vec<Arc<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: u16,
pub attempts: usize,
}
impl Default for ResolveConfig
{
fn default() -> Self
{
return
ResolveConfig
{
nameservers: vec![],
lookup: ResolveConfigLookup::default(),
family: ResolveConfigFamily::default(),
search_list: vec![],
domain: None,
option_flags: OptionFlags::default(),
ndots: 1,
timeout: 5,
attempts: 2,
};
}
}
impl ResolveConfig
{
pub
fn get_resolvers_iter(&self) -> RoundRobinIterator<Arc<ResolveConfEntry>>
{
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());
};
}
fn split_ext(field_data: &str) -> CDnsResult<(&str, u16, Option<String>)>
{
let splited = field_data.split("#").collect::<Vec<&str>>();
let mut port: u16 = 53;
let mut domain: Option<String> = None;
if splited.is_empty() == true
{
return Ok((field_data, port, domain));
}
let mut splt_iter = splited.into_iter();
let without_ext = splt_iter.nth(0).unwrap();
for ext in splt_iter
{
if ext.starts_with("@") == true
{
port =
u16::from_str_radix(&ext[1..], 10).map_err(|e|
internal_error_map!(CDnsErrorType::InternalError, "can not parse port number, {}", e)
)?;
}
else
{
domain = Some(ext.to_string());
}
}
return Ok((without_ext, port, domain));
}
fn split_dns_ifr(field_data: &str, ifr: &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)
)?;
match unsafe { ifr.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 =
field_data.parse().map_err(|e|
internal_error_map!(CDnsErrorType::InternalError, "parse IP: '{}' failed: '{}'", field_data, e)
)?;
let ip_ifr = bind_all(ip_addr);
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) -> CDnsResult<Self>
{
let mut tk = ConfTokenizer::from_str(&file_content)?;
let mut dns_list: Vec<Arc<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: u16 = 5;
let mut attempts: usize = 2;
let if_info: IfInfo = unsafe { IfInfo::get_interfaces_info()? };
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 Some(field_data) = tk.read_next()?
else
{
internal_error!(CDnsErrorType::ConfigError, "unexpected EOF near nameserver");
};
let ext_split_res = Self::split_ext(field_data);
let Ok((without_ext, port, domain)) = ext_split_res
else
{
internal_error!(CDnsErrorType::ConfigError, "{}", ext_split_res.err().unwrap());
};
let split = Self::split_dns_ifr(without_ext, &if_info);
let Ok((dns_ip, ifr_ip)) = split
else
{
internal_error!(CDnsErrorType::ConfigError, "{}", split.err().unwrap());
};
dns_list.push(
Arc::new(ResolveConfEntry::new(SocketAddr::new(dns_ip, port), domain, ifr_ip)?)
);
}
else if field_name == "lookup" {
let lookups = tk.read_upto_eol()?;
if lookups.len() == 0
{
write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - 'lookup' is set but no args provided, setting defaults file, bind"));
continue;
}
lookup_list =
match ResolveConfigLookup::try_from(lookups.as_slice())
{
Ok(r) => r,
Err(e) =>
{
write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - {}", e));
continue;
}
};
}
else if field_name == "family" {
let families = tk.read_upto_eol()?;
if families.len() == 0
{
write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - 'family' is set but no args provided, setting defaults inet4, inet6"));
continue;
}
family_list =
match ResolveConfigFamily::try_from(families.as_slice())
{
Ok(r) => r,
Err(e) =>
{
write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - {}", e));
continue;
}
};
}
else if field_name == "search"
{
let searches = tk.read_upto_eol()?;
if searches.len() == 0
{
write_error!(internal_error_map!(CDnsErrorType::ConfigError, "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
{
write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - 'domain' is defined more than once: {:?} {:?}", domain, domains));
}
if domains.len() > 1
{
write_error!(internal_error_map!(CDnsErrorType::ConfigError, "in /etc/resolv.conf - 'domain' is defined with more than 1 address: {:?}", domains));
}
else if domains.len() == 0
{
write_error!(internal_error_map!(CDnsErrorType::ConfigError, "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
{
write_error!(internal_error_map!(CDnsErrorType::ConfigError, "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) =>
write_error!(e),
Ok(_) => {}
}
},
"debug" =>
{
match Self::set_option("debug", right, &mut option_flags, OptionFlags::OPT_DEBUG)
{
Err(e) =>
write_error!(e),
Ok(_) => {}
}
},
"ndots" =>
{
match Self::read_parameter("ndots", right)
{
Ok(val) =>
{
ndots = if val > 15 { 15 } else { val };
},
Err(e) =>
{
write_error!(e);
}
}
},
"timeout" =>
{
match Self::read_parameter::<u16>("timeout", right)
{
Ok(val) =>
{
timeout = if val > 30 { 30 } else { val };
},
Err(e) =>
{
write_error!(e);
}
}
},
"attempts" =>
{
match Self::read_parameter("attempts", right)
{
Ok(val) =>
{
attempts = if val > 5 { 5 } else { val };
},
Err(e) =>
{
write_error!(e);
}
}
},
"rotate" =>
{
match Self::set_option("rotate", right, &mut option_flags, OptionFlags::OPT_ROTATE)
{
Err(e) =>
write_error!(e),
Ok(_) => {}
}
},
"no-check-names" =>
{
match Self::set_option("no-check-names", right, &mut option_flags, OptionFlags::OPT_NO_CHECK_NAMES)
{
Err(e) =>
write_error!(e),
Ok(_) => {}
}
},
"no-reload" =>
{
match Self::set_option("no-reload", right, &mut option_flags, OptionFlags::OPT_NO_RELOAD)
{
Err(e) =>
write_error!(e),
Ok(_) => {}
}
},
"inet6" =>
{
match Self::set_option("inet6", right, &mut option_flags, OptionFlags::OPT_INET6)
{
Err(e) =>
write_error!(e),
Ok(_) => {}
}
},
"edns0" =>
{
},
"single-request" =>
{
match Self::set_option("single-request", right, &mut option_flags, OptionFlags::OPT_SINGLE_REQUEST)
{
Err(e) =>
write_error!(e),
Ok(_) => {}
}
},
"single-request-reopen" =>
{
match Self::set_option("single-request-reopen", right, &mut option_flags, OptionFlags::OPT_SINGLE_REQUEST_REOPEN)
{
Err(e) =>
write_error!(e),
Ok(_) => {}
}
},
"trust-ad" =>
{
match Self::set_option("trust-ad", right, &mut option_flags, OptionFlags::OPT_TRUST_AD)
{
Err(e) =>
write_error!(e),
Ok(_) => {}
}
},
_ =>
{
write_error!(internal_error_map!(CDnsErrorType::ConfigError, "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,
}
);
}
}
#[cfg(feature = "use_sync")]
#[cfg(test)]
mod tests
{
use std::{net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}, str::FromStr};
use crate::{cfg_resolv_parser::{OptionFlags, ResolveConfigFamily, ResolveConfigLookup}, sync::ResolveConfig};
#[test]
fn test_parser_resolv_tls_0()
{
let ip1 = SocketAddr::new("1.1.1.1".parse::<IpAddr>().unwrap(), 853);
let tls_domain1 = Some(String::from("cloudflare-dns.com"));
let ip2 = SocketAddr::new("2606:4700:4700::1001".parse::<IpAddr>().unwrap(), 853);
let tls_domain2 = Some(String::from("cloudflare-dns.com"));
let ip3 = SocketAddr::new("127.0.0.1".parse().unwrap(), 853);
let tls_domain3 = Some(String::from("localdomain"));
let ip3_if: SocketAddr = SocketAddr::from((Ipv4Addr::from_str("127.0.0.1").unwrap(), 0));
let ip4 = SocketAddr::new("8.8.8.8".parse().unwrap(), 55);
let ip5 = SocketAddr::new("1.0.0.2".parse().unwrap(), 53);
let ip6 = SocketAddr::new("1.1.1.1".parse::<IpAddr>().unwrap(), 443);
let test =
"nameserver 1.1.1.1#@853#cloudflare-dns.com\n\
nameserver 2606:4700:4700::1001#@853#cloudflare-dns.com\n\
nameserver 127.0.0.1%lo#@853#localdomain\n\
nameserver 8.8.8.8#@55\n\
nameserver 1.0.0.2 #@853\n\
nameserver 1.1.1.1#@443#https://cloudflare-dns.com/dns-query\n";
let res = ResolveConfig::parser_resolv_internal(test);
assert_eq!(res.is_ok(), true, "{}", res.err().unwrap());
let res = res.unwrap();
assert_eq!(res.family, ResolveConfigFamily::INET4_INET6 | ResolveConfigFamily::INET6 | ResolveConfigFamily::INET4);
assert_eq!(res.nameservers[0].ip, ip1);
assert_eq!(res.nameservers[0].tls_domain.as_ref(), tls_domain1.as_ref());
assert_eq!(res.nameservers[0].https_req_path, None);
assert_eq!(res.nameservers[1].ip, ip2);
assert_eq!(res.nameservers[1].tls_domain.as_ref(), tls_domain2.as_ref());
assert_eq!(res.nameservers[2].ip, ip3);
assert_eq!(res.nameservers[2].tls_domain.as_ref(), tls_domain3.as_ref());
assert_eq!(res.nameservers[2].adapter_ip, ip3_if);
assert_eq!(res.nameservers[3].ip, ip4);
assert_eq!(res.nameservers[4].ip, ip5);
assert_eq!(res.nameservers[5].ip, ip6);
assert_eq!(res.nameservers[5].tls_domain, Some("cloudflare-dns.com".to_string()));
assert_eq!(res.nameservers[5].https_req_path, Some("dns-query".to_string()));
}
#[test]
fn test_parser_resolv_internal_0()
{
let ip1 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
let ip2 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
let ip3 = SocketAddr::new("8.8.8.8".parse().unwrap(), 53);
let ip4 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
let ip4_if: SocketAddr = SocketAddr::from((Ipv4Addr::from_str("127.0.0.1").unwrap(), 0));
let ip5 = SocketAddr::new("fe80::1".parse().unwrap(), 53);
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 res = ResolveConfig::parser_resolv_internal(test);
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 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
let ip2 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
let ip3 = SocketAddr::new("8.8.8.8".parse().unwrap(), 53);
let test =
"nameserver 192.168.2.1\n\
nameserver 127.0.0.1\n\
lookup bind file\n\
nameserver 8.8.8.8";
let res = ResolveConfig::parser_resolv_internal(test);
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 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
let ip2 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
let ip3 = SocketAddr::new("8.8.8.8".parse().unwrap(), 53);
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 res = ResolveConfig::parser_resolv_internal(test);
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 | ResolveConfigFamily::INET4_INET6);
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 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
let ip2 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
let ip3 = SocketAddr::new("8.8.8.8".parse().unwrap(), 53);
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 res = ResolveConfig::parser_resolv_internal(test);
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_INET6 | 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 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
let ip2 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
let ip3 = SocketAddr::new("8.8.8.8".parse().unwrap(), 53);
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 res = ResolveConfig::parser_resolv_internal(test);
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_INET6 | 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: &[SocketAddr], 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 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
let ip_list: Vec<SocketAddr> = 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 res = ResolveConfig::parser_resolv_internal(test);
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: &[SocketAddr], 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 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
let ip2 = SocketAddr::new("127.0.0.1".parse().unwrap(), 53);
let ip3 = SocketAddr::new("8.8.8.8".parse().unwrap(), 53);
let ip_list: Vec<SocketAddr> = 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 res = ResolveConfig::parser_resolv_internal(test);
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 = SocketAddr::new("192.168.2.1".parse().unwrap(), 53);
let ip2 = SocketAddr::new("192.168.2.2".parse().unwrap(), 53);
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 res = ResolveConfig::parser_resolv_internal(test);
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);
}
}