// SPDX-FileCopyrightText: 2022 Matteo Settenvini // SPDX-License-Identifier: GPL-3.0-or-later #![allow(non_upper_case_globals)] #![allow(non_camel_case_types)] #![allow(non_snake_case)] #![allow(dead_code)] use std::str::FromStr; include!(concat!(env!("OUT_DIR"), "/bindings.rs")); use { anyhow::{anyhow, bail, ensure, Result}, libc::{freeaddrinfo, gai_strerror, getaddrinfo}, nix::sys::socket::{SockaddrLike as _, SockaddrStorage}, std::env, std::ffi::{CStr, CString}, std::net::{IpAddr, Ipv4Addr, Ipv6Addr}, std::path::PathBuf, std::process::Command, std::sync::Once, }; pub type Eai = EaiRetcode; static SETUP: Once = Once::new(); pub fn setup() -> Result<()> { let out_dir = PathBuf::from(env!("OUT_DIR")); let nss_config_status = unsafe { SETUP.call_once(|| { let library_path = test_cdylib::build_current_project(); let mut library_filename = library_path.file_name().unwrap().to_owned(); library_filename.push(".2"); // required for NSS modules let dest = out_dir.join(library_filename); std::fs::copy(library_path, dest).unwrap(); }); let db = CString::new("hosts").unwrap(); let resolvers = CString::new("malcontent dns").unwrap(); __nss_configure_lookup(db.as_ptr(), resolvers.as_ptr()) }; ensure!( nss_config_status == 0, "Unable to configure NSS to load module: __nss_configure_lookup() returned {}", nss_config_status ); Ok(()) } pub fn system_resolve(host: &str) -> Result { 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 { 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)) } }