Getting network DNS addresses for a Windows host in Rust
Tags: rust, window, api
Published: 2023-07-13 16:45:21

As you probably know, Windows is seriously considering and investing in Rust. The [Windows Rust crate](https://microsoft.github.io/windows-docs-rs/doc/windows/index.html) 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](https://learn.microsoft.com/en-us/windows/win32/api/iphlpapi/nf-iphlpapi-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:

```toml
[dependencies.windows]
version = "0.48"
features = [
    "Win32_Foundation",
    "Win32_NetworkManagement_IpHelper",
    "Win32_NetworkManagement_Ndis",
    "Win32_Networking_WinSock",
]
```

The Rust equivalent API to call is:

```rust
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:

```rust
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:

```rust
// 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:

```rust
// 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:

```rust
    // 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 <a href="https://unsplash.com/@nathanfertig?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Nathan Fertig</a> on <a href="https://unsplash.com/fr/photos/U8zsjmKA840?utm_source=unsplash&utm_medium=referral&utm_content=creditCopyText">Unsplash</a>