malcontent-dns-parental-con.../tests/integration_test.rs
Matteo Settenvini 6582c27a43
Update deps and fix getaddrinfo test for canary
Apparently the return of getaddrinfo for the canary domain has
changed in the latest versions of either glibc or the corresponding
crate.

The error returned is Eai::NoData rather than Eai::NoName.
2023-01-10 17:13:05 +01:00

195 lines
6.7 KiB
Rust

// SPDX-FileCopyrightText: 2022 Matteo Settenvini <matteo.settenvini@montecristosoftware.eu>
// SPDX-License-Identifier: GPL-3.0-or-later
mod common;
use {
crate::common::{Eai, Restriction, Restrictions},
anyhow::Result,
libc::{freeaddrinfo, gai_strerror, getaddrinfo},
nix::unistd::getuid,
once_cell::sync::Lazy,
std::collections::HashMap,
std::net::{IpAddr, Ipv4Addr, Ipv6Addr},
};
static CLOUDFLARE_PARENTALCONTROL_ADDRS: Lazy<Restrictions> = Lazy::new(|| {
vec![
Restriction {
ip: IpAddr::V4(Ipv4Addr::new(1, 1, 1, 3)),
hostname: "family.cloudflare-dns.com".into(),
},
Restriction {
ip: IpAddr::V4(Ipv4Addr::new(1, 0, 0, 3)),
hostname: "family.cloudflare-dns.com".into(),
},
Restriction {
ip: IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1113)),
hostname: "family.cloudflare-dns.com".into(),
},
Restriction {
ip: IpAddr::V6(Ipv6Addr::new(0x2606, 0x4700, 0x4700, 0, 0, 0, 0, 0x1003)),
hostname: "family.cloudflare-dns.com".into(),
},
]
});
static NO_RESTRICTIONS: Lazy<Restrictions> = Lazy::new(|| vec![]);
fork_test! {
#[test]
fn nss_module_is_loaded() -> Result<()> {
common::setup()?;
tokio::runtime::Runtime::new().unwrap().block_on(async {
let _dbus = common::mock_dbus(HashMap::from([(getuid(), vec![NO_RESTRICTIONS.clone()])])).await?;
common::resolve_with_module(libc::AF_INET, "gnome.org")?;
Ok(())
})
}
#[test]
fn application_dns_is_nodata() -> Result<()> {
common::setup()?;
tokio::runtime::Runtime::new().unwrap().block_on(async {
let _dbus = common::mock_dbus(HashMap::from([(
getuid(),
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
)])).await?;
let hostname = std::ffi::CString::new("use-application-dns.net").unwrap();
unsafe {
let mut addr = std::ptr::null_mut();
let getaddrinfo_status = getaddrinfo(
hostname.as_ptr(),
std::ptr::null(),
std::ptr::null(),
&mut addr,
);
let error = std::ffi::CStr::from_ptr(gai_strerror(getaddrinfo_status));
assert_eq!(
getaddrinfo_status,
Eai::NoData.0,
"Should have gotten no data (NODATA), instead got {}",
error.to_str().unwrap()
);
freeaddrinfo(addr);
};
Ok(())
})
}
#[test]
fn getaddrinfo_resolution() -> Result<()> {
common::setup()?;
const HOSTNAME: &str = "gnome.org";
tokio::runtime::Runtime::new().unwrap().block_on(async {
let _dbus = common::mock_dbus(HashMap::from([(
getuid(),
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
)])).await?;
unsafe {
let mut addr = std::ptr::null_mut();
let getaddrinfo_status = getaddrinfo(
std::ffi::CString::new(HOSTNAME)?.as_ptr(),
std::ptr::null(),
std::ptr::null(),
&mut addr,
);
let error = std::ffi::CStr::from_ptr(gai_strerror(getaddrinfo_status));
assert_eq!(
getaddrinfo_status,
Eai::Success.0,
"Should have gotten an hostname, instead got {}",
error.to_str().unwrap()
);
freeaddrinfo(addr);
};
Ok(())
})
}
#[test]
fn wikipedia_is_unrestricted() -> Result<()> {
common::setup()?;
const HOSTNAME: &str = "wikipedia.org";
tokio::runtime::Runtime::new().unwrap().block_on(async {
let _dbus = common::mock_dbus(HashMap::from([(
getuid(),
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
)])).await?;
for family in [libc::AF_INET, libc::AF_INET6] {
let system_addr = common::resolve_with_system(family, HOSTNAME)?;
let our_addrs = common::resolve_with_module(family, HOSTNAME)?;
assert!(our_addrs.contains(&system_addr));
}
Ok(())
})
}
#[test]
fn adultsite_is_restricted_ipv4() -> Result<()> {
common::setup()?;
const HOSTNAME: &str = "nudity.testcategory.com";
tokio::runtime::Runtime::new().unwrap().block_on(async {
let _dbus = common::mock_dbus(HashMap::from([(
getuid(),
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
)])).await?;
let system_addr = common::resolve_with_system(libc::AF_INET, HOSTNAME)?;
let our_addrs = common::resolve_with_module(libc::AF_INET, HOSTNAME)?;
assert!(!our_addrs.contains(&system_addr), "Resolver answered with {:?}, should not contain {}", our_addrs, system_addr);
assert_eq!(our_addrs, [IpAddr::V4(Ipv4Addr::UNSPECIFIED)]);
Ok(())
})
}
#[test]
fn adultsite_is_restricted_ipv6() -> Result<()> {
common::setup()?;
const HOSTNAME: &str = "nudity.testcategory.com";
tokio::runtime::Runtime::new().unwrap().block_on(async {
let _dbus = common::mock_dbus(HashMap::from([(
getuid(),
vec![CLOUDFLARE_PARENTALCONTROL_ADDRS.clone()],
)])).await;
let system_addr = common::resolve_with_system(libc::AF_INET6, HOSTNAME)?;
let our_addrs = common::resolve_with_module(libc::AF_INET6, HOSTNAME)?;
assert!(!our_addrs.contains(&system_addr), "Resolver answered with {:?}, should not contain {}", our_addrs, system_addr);
assert_eq!(our_addrs, [IpAddr::V6(Ipv6Addr::UNSPECIFIED)]);
Ok(())
})
}
#[test]
fn privileged_user_bypasses_restrictions() -> Result<()> {
common::setup()?;
const HOSTNAME: &str = "malware.testcategory.com";
tokio::runtime::Runtime::new().unwrap().block_on(async {
let _dbus = common::mock_dbus(HashMap::from([(getuid(), vec![NO_RESTRICTIONS.clone()])])).await?;
for family in [libc::AF_INET, libc::AF_INET6] {
let system_addr = common::resolve_with_system(family, HOSTNAME)?;
let our_addrs = common::resolve_with_module(family, HOSTNAME)?;
assert!(our_addrs.contains(&system_addr));
}
Ok(())
})
}
}