use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
#[repr(C, align(8))]
struct ResState([u8; 552]);
#[repr(C, align(4))]
#[derive(Clone, Copy)]
struct ResSockaddrUnion([u8; 128]);
const MAXNS: usize = 3;
#[link(name = "resolv")]
unsafe extern "C" {
fn res_9_ninit(state: *mut ResState) -> libc::c_int;
fn res_9_getservers(
state: *mut ResState,
set: *mut ResSockaddrUnion,
cnt: libc::c_int,
) -> libc::c_int;
fn res_9_ndestroy(state: *mut ResState);
}
pub fn get_system_dns_conf() -> Vec<IpAddr> {
let mut state = ResState([0; 552]);
let mut servers = [ResSockaddrUnion([0; 128]); MAXNS];
let count = unsafe {
if res_9_ninit(&mut state) != 0 {
return Vec::new();
}
let count = res_9_getservers(&mut state, servers.as_mut_ptr(), MAXNS as libc::c_int);
res_9_ndestroy(&mut state);
count
};
servers
.iter()
.take(usize::try_from(count).unwrap_or(0))
.filter_map(sockaddr_ip)
.collect()
}
fn sockaddr_ip(addr: &ResSockaddrUnion) -> Option<IpAddr> {
match i32::from(addr.0[1]) {
libc::AF_INET => {
let sin: libc::sockaddr_in =
unsafe { std::ptr::read_unaligned(addr.0.as_ptr().cast()) };
Some(IpAddr::V4(Ipv4Addr::from(u32::from_be(
sin.sin_addr.s_addr,
))))
}
libc::AF_INET6 => {
let sin6: libc::sockaddr_in6 =
unsafe { std::ptr::read_unaligned(addr.0.as_ptr().cast()) };
Some(IpAddr::V6(Ipv6Addr::from(sin6.sin6_addr.s6_addr)))
}
_ => None,
}
}