use anyhow::{bail, Result};
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
mod bindings {
windows::include_bindings!();
}
use bindings::Windows::Win32::{
NetworkManagement::IpHelper::*,
Networking::WinSock::{SOCKADDR_IN, SOCKADDR_IN6},
System::Diagnostics::Debug::*,
};
pub fn get_dns_servers() -> Result<Vec<IpAddr>> {
let flags = GAA_FLAG_INCLUDE_TUNNEL_BINDINGORDER;
let ans = unsafe {
let mut buf_size = 0;
let error = GetAdaptersAddresses(
AF_UNSPEC,
flags,
std::ptr::null_mut(),
std::ptr::null_mut(),
&mut buf_size,
);
match WIN32_ERROR::from(error) {
ERROR_BUFFER_OVERFLOW => {}
e => {
bail!("GetAdaptersAddresses#1 failed: {:?}", e);
}
}
let block_size = std::mem::size_of::<IP_ADAPTER_ADDRESSES_LH>() as u32;
let new_capacity = buf_size + block_size;
let mut buf = Vec::<u8>::with_capacity(new_capacity as usize);
let (prefix, body, _) = buf.align_to_mut::<IP_ADAPTER_ADDRESSES_LH>();
let mut buf_size = new_capacity - prefix.len() as u32;
let error = GetAdaptersAddresses(
AF_UNSPEC,
flags,
std::ptr::null_mut(),
body.as_mut_ptr(),
&mut buf_size,
);
match WIN32_ERROR::from(error) {
NO_ERROR => {}
e => {
bail!("GetAdaptersAddresses#2 failed: {:?}", e);
}
}
let mut ans = Vec::new();
let mut p_adapter = body.as_mut_ptr();
while !p_adapter.is_null() {
let adapter_dns_servers = get_adapter_dns_servers(&*p_adapter);
for sa in &adapter_dns_servers {
if !ans.contains(sa) {
ans.push(*sa);
}
}
p_adapter = (*p_adapter).Next;
}
ans
};
Ok(ans)
}
unsafe fn get_adapter_dns_servers(a: &IP_ADAPTER_ADDRESSES_LH) -> Vec<IpAddr> {
let mut p_address = a.FirstDnsServerAddress;
let mut ans = Vec::new();
while !p_address.is_null() {
let sock_addr = (*p_address).Address.lpSockaddr;
if !sock_addr.is_null() {
match ADDRESS_FAMILY::from((*sock_addr).sa_family as u32) {
AF_INET => {
let p_sockaddr_in: *const SOCKADDR_IN = sock_addr.cast();
let ipv4 = Ipv4Addr::from(u32::from_be((*p_sockaddr_in).sin_addr.S_un.S_addr));
if !ipv4.is_unspecified() {
ans.push(ipv4.into());
}
}
AF_INET6 => {
let p_sockaddr_in6: *const SOCKADDR_IN6 = sock_addr.cast();
let ipv6 = Ipv6Addr::from((*p_sockaddr_in6).sin6_addr.u.Byte);
if !ipv6.is_unspecified() && !is_unicast_site_local(&ipv6) {
ans.push(ipv6.into());
}
}
_ => {}
}
}
p_address = (*p_address).Next;
}
ans
}
#[inline]
fn is_unicast_site_local(ipv6: &Ipv6Addr) -> bool {
(ipv6.segments()[0] & 0xffc0) == 0xfec0
}