2022-08-13 17:04:22 +02:00
|
|
|
// SPDX-FileCopyrightText: 2022 Matteo Settenvini <matteo.settenvini@montecristosoftware.eu>
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
2022-08-14 23:03:59 +02:00
|
|
|
#![allow(non_upper_case_globals)]
|
|
|
|
#![allow(non_camel_case_types)]
|
|
|
|
#![allow(non_snake_case)]
|
|
|
|
#![allow(dead_code)]
|
|
|
|
|
2022-08-15 22:55:35 +02:00
|
|
|
use std::str::FromStr;
|
|
|
|
|
|
|
|
include!(concat!(env!("OUT_DIR"), "/bindings.rs"));
|
|
|
|
|
2022-08-13 17:04:22 +02:00
|
|
|
use {
|
2022-08-15 22:55:35 +02:00
|
|
|
anyhow::{anyhow, bail, ensure, Result},
|
|
|
|
libc::{freeaddrinfo, gai_strerror, getaddrinfo},
|
|
|
|
nix::sys::socket::{SockaddrLike as _, SockaddrStorage},
|
2022-08-14 23:03:59 +02:00
|
|
|
std::env,
|
2022-08-15 22:55:35 +02:00
|
|
|
std::ffi::{CStr, CString},
|
|
|
|
std::net::{IpAddr, Ipv4Addr, Ipv6Addr},
|
2022-08-13 17:04:22 +02:00
|
|
|
std::path::PathBuf,
|
2022-08-15 22:55:35 +02:00
|
|
|
std::process::Command,
|
2022-08-13 17:04:22 +02:00
|
|
|
std::sync::Once,
|
|
|
|
};
|
|
|
|
|
2022-08-15 22:55:35 +02:00
|
|
|
pub type Eai = EaiRetcode;
|
2022-08-13 17:04:22 +02:00
|
|
|
|
|
|
|
static SETUP: Once = Once::new();
|
|
|
|
|
|
|
|
pub fn setup() -> Result<()> {
|
2022-08-14 23:03:59 +02:00
|
|
|
let out_dir = PathBuf::from(env!("OUT_DIR"));
|
|
|
|
let nss_config_status = unsafe {
|
2022-08-13 17:04:22 +02:00
|
|
|
SETUP.call_once(|| {
|
|
|
|
let library_path = test_cdylib::build_current_project();
|
|
|
|
let mut library_filename = library_path.file_name().unwrap().to_owned();
|
2022-08-14 23:03:59 +02:00
|
|
|
library_filename.push(".2"); // required for NSS modules
|
2022-08-13 17:04:22 +02:00
|
|
|
|
|
|
|
let dest = out_dir.join(library_filename);
|
2022-08-14 23:03:59 +02:00
|
|
|
std::fs::copy(library_path, dest).unwrap();
|
|
|
|
});
|
2022-08-13 17:04:22 +02:00
|
|
|
|
2022-08-14 23:03:59 +02:00
|
|
|
let db = CString::new("hosts").unwrap();
|
2022-08-15 22:55:35 +02:00
|
|
|
let resolvers = CString::new("malcontent dns").unwrap();
|
2022-08-14 23:03:59 +02:00
|
|
|
__nss_configure_lookup(db.as_ptr(), resolvers.as_ptr())
|
|
|
|
};
|
2022-08-13 17:04:22 +02:00
|
|
|
|
2022-08-14 23:03:59 +02:00
|
|
|
ensure!(
|
|
|
|
nss_config_status == 0,
|
|
|
|
"Unable to configure NSS to load module: __nss_configure_lookup() returned {}",
|
|
|
|
nss_config_status
|
|
|
|
);
|
2022-08-13 17:04:22 +02:00
|
|
|
|
2022-08-14 23:03:59 +02:00
|
|
|
Ok(())
|
2022-08-13 17:04:22 +02:00
|
|
|
}
|
2022-08-15 22:55:35 +02:00
|
|
|
|
|
|
|
pub fn system_resolve(host: &str) -> Result<IpAddr> {
|
|
|
|
let process = Command::new("getent").arg("hosts").arg(host).output()?;
|
|
|
|
ensure!(
|
|
|
|
process.status.success(),
|
|
|
|
"Failed to run getent to check host IP"
|
|
|
|
);
|
|
|
|
let output = String::from_utf8(process.stdout)?;
|
|
|
|
let addr_string = output
|
|
|
|
.as_str()
|
|
|
|
.split(' ')
|
|
|
|
.next()
|
|
|
|
.ok_or(anyhow!("Unparseable output from getent"))?;
|
|
|
|
Ok(IpAddr::from_str(&addr_string)?)
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn convert_addrinfo(sa: &SockaddrStorage) -> Result<IpAddr> {
|
|
|
|
if let Some(addr) = sa.as_sockaddr_in() {
|
|
|
|
Ok(IpAddr::V4(Ipv4Addr::from(addr.ip())))
|
|
|
|
} else if let Some(addr) = sa.as_sockaddr_in6() {
|
|
|
|
Ok(IpAddr::V6(Ipv6Addr::from(addr.ip())))
|
|
|
|
} else {
|
|
|
|
bail!("addrinfo is not either an IPv4 or IPv6 address")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn resolve_system_and_us(hostname: &str) -> Result<(IpAddr, IpAddr)> {
|
|
|
|
setup()?;
|
|
|
|
let ip_from_system = system_resolve(hostname)?;
|
|
|
|
|
|
|
|
let c_hostname = CString::new(hostname).unwrap();
|
|
|
|
unsafe {
|
|
|
|
let mut addr = std::ptr::null_mut();
|
|
|
|
let getaddrinfo_status = getaddrinfo(
|
|
|
|
c_hostname.as_ptr(),
|
|
|
|
std::ptr::null(),
|
|
|
|
std::ptr::null(),
|
|
|
|
&mut addr,
|
|
|
|
);
|
|
|
|
|
|
|
|
let error = CStr::from_ptr(gai_strerror(getaddrinfo_status));
|
|
|
|
assert_eq!(
|
|
|
|
getaddrinfo_status,
|
|
|
|
Eai::Success.0,
|
|
|
|
"Should have gotten hostname for {}, instead got {}",
|
|
|
|
hostname,
|
|
|
|
error.to_str().unwrap()
|
|
|
|
);
|
|
|
|
let addr_storage = SockaddrStorage::from_raw((*addr).ai_addr, Some((*addr).ai_addrlen))
|
|
|
|
.ok_or(anyhow!("Garbled addrinfo from getaddrinfo()"))?;
|
|
|
|
let ip_from_us = convert_addrinfo(&addr_storage)?;
|
|
|
|
freeaddrinfo(addr);
|
|
|
|
Ok((ip_from_system, ip_from_us))
|
|
|
|
}
|
|
|
|
}
|