resolv-conf/tests/lib.rs
2025-03-13 10:46:59 +01:00

437 lines
13 KiB
Rust

extern crate resolv_conf;
use resolv_conf::{Family, Lookup, Network, ScopedIp};
use std::fs::File;
use std::io::Read;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use std::path::Path;
#[test]
fn test_comment() {
resolv_conf::Config::parse("#").unwrap();
resolv_conf::Config::parse(";").unwrap();
resolv_conf::Config::parse("#junk").unwrap();
resolv_conf::Config::parse("# junk").unwrap();
resolv_conf::Config::parse(";junk").unwrap();
resolv_conf::Config::parse("; junk").unwrap();
}
fn ip(s: &str) -> ScopedIp {
s.parse().unwrap()
}
fn parse_str(s: &str) -> resolv_conf::Config {
resolv_conf::Config::parse(s).unwrap()
}
#[test]
fn test_basic_options() {
assert_eq!(
parse_str("nameserver 127.0.0.1").nameservers,
vec![ip("127.0.0.1")]
);
assert_eq!(
parse_str("search localnet.*").get_search(),
Some(vec!["localnet.*".to_string()]).as_ref()
);
assert_eq!(
parse_str("domain example.com.").get_domain(),
Some(String::from("example.com.")).as_ref()
);
}
#[test]
fn test_trust_ad() {
assert!(parse_str("options edns0 trust-ad").trust_ad);
}
#[test]
fn test_no_reload() {
assert!(parse_str("options no-reload").no_reload);
}
#[test]
fn test_extra_whitespace() {
assert_eq!(
parse_str("domain example.com.").get_domain(),
Some(String::from("example.com.")).as_ref()
);
assert_eq!(
parse_str("domain example.com. ").get_domain(),
Some(String::from("example.com.")).as_ref()
);
// hard tabs
assert_eq!(
parse_str(" domain example.com. ").get_domain(),
Some(String::from("example.com.")).as_ref()
);
// hard tabs + spaces
assert_eq!(
parse_str(" domain example.com. ").get_domain(),
Some(String::from("example.com.")).as_ref()
);
}
#[test]
fn test_invalid_lines() {
assert!(resolv_conf::Config::parse("nameserver 10.0.0.1%1").is_err());
assert!(resolv_conf::Config::parse("nameserver 10.0.0.1.0").is_err());
assert!(resolv_conf::Config::parse("Nameserver 10.0.0.1").is_err());
assert!(resolv_conf::Config::parse("nameserver 10.0.0.1 domain foo.com").is_err());
assert!(resolv_conf::Config::parse("invalid foo.com").is_err());
assert!(resolv_conf::Config::parse("options ndots:1 foo:1").is_err());
}
#[test]
fn test_empty_line() {
assert_eq!(parse_str(""), resolv_conf::Config::new());
}
#[test]
fn test_multiple_options_on_one_line() {
let config = parse_str("options ndots:8 attempts:8 rotate inet6 no-tld-query timeout:8");
assert_eq!(config.ndots, 8);
assert_eq!(config.timeout, 8);
assert_eq!(config.attempts, 8);
assert!(config.rotate);
assert!(config.inet6);
assert!(config.no_tld_query);
}
#[test]
fn test_ip() {
let parsed = ip("FE80::C001:1DFF:FEE0:0%eth0");
let address = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xc001, 0x1dff, 0xfee0, 0);
let scope = "eth0".to_string();
assert_eq!(parsed, ScopedIp::V6(address, Some(scope)));
let parsed = ip("FE80::C001:1DFF:FEE0:0%1");
let address = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xc001, 0x1dff, 0xfee0, 0);
let scope = "1".to_string();
assert_eq!(parsed, ScopedIp::V6(address, Some(scope)));
let parsed = ip("FE80::C001:1DFF:FEE0:0");
let address = Ipv6Addr::new(0xfe80, 0, 0, 0, 0xc001, 0x1dff, 0xfee0, 0);
assert_eq!(parsed, ScopedIp::V6(address, None));
assert!("10.0.0.1%1".parse::<ScopedIp>().is_err());
assert!("10.0.0.1%eth0".parse::<ScopedIp>().is_err());
assert!("FE80::C001:1DFF:FEE0:0%".parse::<ScopedIp>().is_err());
assert!("FE80::C001:1DFF:FEE0:0% ".parse::<ScopedIp>().is_err());
let parsed = ip("192.168.10.1");
let address = Ipv4Addr::new(192, 168, 10, 1);
assert_eq!(parsed, ScopedIp::V4(address));
}
#[test]
fn test_nameserver() {
assert_eq!(
parse_str("nameserver 127.0.0.1").nameservers[0],
ip("127.0.0.1")
);
assert_eq!(
parse_str("nameserver 127.0.0.1#comment").nameservers[0],
ip("127.0.0.1")
);
assert_eq!(
parse_str("nameserver 127.0.0.1;comment").nameservers[0],
ip("127.0.0.1")
);
assert_eq!(
parse_str("nameserver 127.0.0.1 # another comment").nameservers[0],
ip("127.0.0.1")
);
assert_eq!(
parse_str("nameserver 127.0.0.1 ; ").nameservers[0],
ip("127.0.0.1")
);
assert_eq!(parse_str("nameserver ::1").nameservers[0], ip("::1"));
assert_eq!(
parse_str("nameserver 2001:db8:85a3:8d3:1319:8a2e:370:7348").nameservers[0],
ip("2001:db8:85a3:8d3:1319:8a2e:370:7348")
);
assert_eq!(
parse_str("nameserver ::ffff:192.0.2.128").nameservers[0],
ip("::ffff:192.0.2.128")
);
}
fn parse_file<P: AsRef<Path>>(path: P) -> resolv_conf::Config {
let mut data = String::new();
let mut file = File::open(path).unwrap();
file.read_to_string(&mut data).unwrap();
resolv_conf::Config::parse(&data).unwrap()
}
#[test]
fn test_parse_simple_conf() {
let mut config = resolv_conf::Config::new();
config
.nameservers
.push(ScopedIp::V4(Ipv4Addr::new(8, 8, 8, 8)));
config
.nameservers
.push(ScopedIp::V4(Ipv4Addr::new(8, 8, 4, 4)));
assert_eq!(config, parse_file("tests/resolv.conf-simple"));
}
#[test]
fn test_parse_linux_conf() {
let mut config = resolv_conf::Config::new();
config.set_domain(String::from("example.com"));
config.set_search(vec!["example.com".into(), "sub.example.com".into()]);
config.nameservers = vec![
ScopedIp::V6(
Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888),
None,
),
ScopedIp::V6(
Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8844),
None,
),
ScopedIp::V4(Ipv4Addr::new(8, 8, 8, 8)),
ScopedIp::V4(Ipv4Addr::new(8, 8, 4, 4)),
];
config.ndots = 8;
config.timeout = 8;
config.attempts = 8;
config.rotate = true;
config.inet6 = true;
config.no_tld_query = true;
config.sortlist = vec![
Network::V4(
Ipv4Addr::new(130, 155, 160, 0),
Ipv4Addr::new(255, 255, 240, 0),
),
// This fails currently
Network::V4(Ipv4Addr::new(130, 155, 0, 0), Ipv4Addr::new(255, 255, 0, 0)),
];
assert_eq!(config, parse_file("tests/resolv.conf-linux"));
}
#[test]
fn test_parse_macos_conf() {
let mut config = resolv_conf::Config::new();
config.set_domain(String::from("example.com."));
config.set_search(vec!["example.com.".into(), "sub.example.com.".into()]);
config.nameservers = vec![
ScopedIp::V6(
Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888),
None,
),
ScopedIp::V6(
Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8844),
None,
),
ScopedIp::V4(Ipv4Addr::new(8, 8, 8, 8)),
ScopedIp::V4(Ipv4Addr::new(8, 8, 4, 4)),
];
config.ndots = 8;
config.timeout = 8;
config.attempts = 8;
assert_eq!(config, parse_file("tests/resolv.conf-macos"));
}
#[test]
fn test_openbsd_conf() {
let mut config = resolv_conf::Config::new();
config.nameservers = vec![
ScopedIp::V4(Ipv4Addr::new(8, 8, 8, 8)),
ScopedIp::V4(Ipv4Addr::new(8, 8, 4, 4)),
];
config.lookup = vec![Lookup::File, Lookup::Bind];
assert_eq!(config, parse_file("tests/resolv.conf-openbsd"));
}
#[test]
fn test_openbsd_grammar() {
let mut config = resolv_conf::Config::new();
config.lookup = vec![Lookup::File, Lookup::Bind];
assert_eq!(
resolv_conf::Config::parse("lookup file bind").unwrap(),
config
);
let mut config = resolv_conf::Config::new();
config.lookup = vec![Lookup::Bind];
assert_eq!(resolv_conf::Config::parse("lookup bind").unwrap(), config);
let mut config = resolv_conf::Config::new();
config.lookup = vec![Lookup::Extra(String::from("unexpected"))];
assert_eq!(
resolv_conf::Config::parse("lookup unexpected").unwrap(),
config
);
let mut config = resolv_conf::Config::new();
config.family = vec![Family::Inet4, Family::Inet6];
assert_eq!(
resolv_conf::Config::parse("family inet4 inet6").unwrap(),
config
);
let mut config = resolv_conf::Config::new();
config.family = vec![Family::Inet4];
assert_eq!(resolv_conf::Config::parse("family inet4").unwrap(), config);
let mut config = resolv_conf::Config::new();
config.family = vec![Family::Inet6];
assert_eq!(resolv_conf::Config::parse("family inet6").unwrap(), config);
assert!(resolv_conf::Config::parse("family invalid").is_err());
}
#[test]
fn test_glibc_normalize() {
let mut config = resolv_conf::Config::new();
config.nameservers = vec![
ScopedIp::V6(
Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888),
None,
),
ScopedIp::V6(
Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8844),
None,
),
ScopedIp::V4(Ipv4Addr::new(8, 8, 8, 8)),
ScopedIp::V4(Ipv4Addr::new(8, 8, 4, 4)),
];
config.set_search(vec![
"a.example.com".into(),
"b.example.com".into(),
"c.example.com".into(),
"d.example.com".into(),
"e.example.com".into(),
"f.example.com".into(),
"g.example.com".into(),
"h.example.com".into(),
]);
config.glibc_normalize();
assert_eq!(
vec![
ScopedIp::V6(
Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8888),
None,
),
ScopedIp::V6(
Ipv6Addr::new(0x2001, 0x4860, 0x4860, 0, 0, 0, 0, 0x8844),
None,
),
ScopedIp::V4(Ipv4Addr::new(8, 8, 8, 8)),
],
config.nameservers
);
assert_eq!(
Some(&vec![
"a.example.com".into(),
"b.example.com".into(),
"c.example.com".into(),
"d.example.com".into(),
"e.example.com".into(),
"f.example.com".into()
]),
config.get_search()
);
}
#[test]
fn test_get_nameservers_or_local() {
let config = resolv_conf::Config::new();
assert_eq!(
vec![
ScopedIp::from(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))),
ScopedIp::from(IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1))),
],
config.get_nameservers_or_local()
);
}
#[test]
#[cfg(feature = "system")]
#[ignore]
fn test_get_system_domain() {
let config = resolv_conf::Config::new();
assert_eq!(Some("lan".into()), config.get_system_domain());
}
#[test]
fn test_default_display() {
let original_config = resolv_conf::Config::new();
let output = original_config.to_string();
let restored_config = resolv_conf::Config::parse(&output).unwrap();
assert_eq!(original_config, restored_config);
}
#[test]
fn test_non_default_display() {
let mut original_config = resolv_conf::Config::new();
original_config.nameservers = vec![
ip("192.168.0.94"),
ip("fe80::0123:4567:89ab:cdef"),
ip("fe80::0123:4567:89ab:cdef%zone"),
];
original_config.sortlist = vec![
Network::V4(
"192.168.1.94".parse().unwrap(),
"255.255.252.0".parse().unwrap(),
),
Network::V6("fe80::0123".parse().unwrap(), "fe80::cdef".parse().unwrap()),
];
original_config.set_domain("my.domain".to_owned());
original_config.set_search(
vec!["my.domain", "alt.domain"]
.into_iter()
.map(str::to_owned)
.collect(),
);
original_config.debug = true;
original_config.ndots = 4;
original_config.timeout = 20;
original_config.attempts = 5;
original_config.rotate = true;
original_config.no_check_names = true;
original_config.inet6 = true;
original_config.ip6_bytestring = true;
original_config.ip6_dotint = true;
original_config.edns0 = true;
original_config.single_request = true;
original_config.single_request_reopen = true;
original_config.no_tld_query = true;
original_config.use_vc = true;
let output = original_config.to_string();
println!("Output:\n\n{}", output);
let restored_config = resolv_conf::Config::parse(&output).unwrap();
assert_eq!(original_config, restored_config);
}
#[test]
fn test_display_preservers_last_search() {
let mut original_config = resolv_conf::Config::new();
original_config.set_search(
vec!["my.domain", "alt.domain"]
.into_iter()
.map(str::to_owned)
.collect(),
);
original_config.set_domain("my.domain".to_owned());
let output = original_config.to_string();
println!("Output:\n\n{}", output);
let restored_config = resolv_conf::Config::parse(&output).unwrap();
assert_eq!(original_config, restored_config);
}