Dandy Vica
Posted on July 13, 2023
As you probably know, Windows is seriously considering and investing in Rust. The Windows Rust crate is one example of this commitment.
It allows you to call most if not all Windows APIs from Rust natively.
One caveat though: this crate is full of unsafe code.
In one of my future crate, I needed to get the DNS servers of a host. In Linux, it's quite straightforward, looking into the /etc/resolv.conf file.
But in Windows, it's more complicated. One Windows API is meant to get the details of each network interface in the system. it's called
GetAdaptersAddresses.
Let's explain how I achieved this.
First create a binary crate:
$ cargo new dnssrv
Then, add the following to the Cargo.toml file:
[dependencies.windows]
version = "0.48"
features = [
"Win32_Foundation",
"Win32_NetworkManagement_IpHelper",
"Win32_NetworkManagement_Ndis",
"Win32_Networking_WinSock",
]
The Rust equivalent API to call is:
pub unsafe fn GetAdaptersAddresses(
family: u32,
flags: GET_ADAPTERS_ADDRESSES_FLAGS,
reserved: Option<*const c_void>,
adapteraddresses: Option<*mut IP_ADAPTER_ADDRESSES_LH>,
sizepointer: *mut u32
) -> u32
All the different parameters are explained in the Microsoft Windows API link.
To use the API and Windows types, add the following:
use windows::Win32::{
Foundation::{ERROR_BUFFER_OVERFLOW, ERROR_SUCCESS},
NetworkManagement::IpHelper::{
GetAdaptersAddresses, GAA_FLAG_INCLUDE_PREFIX,
IP_ADAPTER_ADDRESSES_LH,
},
Networking::WinSock::AF_INET,
};
The trick is how to convert raw pointer to structures. It's actually quite simple using the as_mut_ptr() function on a vector.
As explained in the Windows API page, first call the API with a low buffer length:
// first call with a empty buffer
let family = AF_INET.0 as u32; // look for IPV4 addresses
let mut buflen = 0u32;
let mut rc =
unsafe { GetAdaptersAddresses(family, GAA_FLAG_INCLUDE_PREFIX, None, None, &mut buflen) };
This should return a ERROR_BUFFER_OVERFLOW return code, with the true value needed for the buffer:
// second with the actual buffer size large enough to hold data
if rc == ERROR_BUFFER_OVERFLOW.0 {
let mut addr = vec![0u8; buflen as usize]; // beware though of the conversion between u32 and usize
let ptr = addr.as_mut_ptr() as *mut IP_ADAPTER_ADDRESSES_LH;
rc = unsafe {
GetAdaptersAddresses(
family,
GAA_FLAG_INCLUDE_PREFIX,
None,
Some(ptr),
&mut buflen,
)
};
After that it's just a matter of moving through the linked list of adapters, and for each adapter, the link list of DNS ip addresses:
// second with the actual buffer size large enough to hold data
if rc == ERROR_SUCCESS.0 {
// loop through adapters and grab DNS addresses
let mut p = ptr;
while !p.is_null() {
unsafe {
let mut p_dns = (*p).FirstDnsServerAddress;
// loop through DNS addresses for this adapter
while !p_dns.is_null() {
let sockaddr = (*p_dns).Address.lpSockaddr;
println!("found DNS server: {:?} for adapter '{}'", (*sockaddr).sa_data, (*p).Description.display());
p_dns = (*p_dns).Next;
}
p = (*p).Next;
}
}
} else {
println!("error {} calling GetAdaptersAddresses()", rc);
}
You can grab the full code here: https://github.com/dandyvica/get_dns_ip
Hope this helps !
Photo by Nathan Fertig on Unsplash
Posted on July 13, 2023
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.