+use std::collections::HashMap;
+use std::fmt::Write;
+use std::net::IpAddr;
+
+use windows::Win32::Foundation::{
+ ERROR_ADDRESS_NOT_ASSOCIATED, ERROR_BUFFER_OVERFLOW, ERROR_INVALID_PARAMETER,
+ ERROR_NOT_ENOUGH_MEMORY, ERROR_NO_DATA, ERROR_SUCCESS, WIN32_ERROR,
+};
+use windows::Win32::NetworkManagement::IpHelper::{
+ GetAdaptersAddresses, IP_ADAPTER_UNICAST_ADDRESS_LH,
+};
+use windows::Win32::NetworkManagement::IpHelper::{
+ GAA_FLAG_SKIP_ANYCAST, GAA_FLAG_SKIP_MULTICAST, IP_ADAPTER_ADDRESSES_LH,
+};
+use windows::Win32::Networking::WinSock::{AF_INET, AF_INET6, SOCKADDR, SOCKADDR_IN, SOCKADDR_IN6};
+
+use crate::{Error, IfIndex, Interface};
+
+pub(crate) fn list_interfaces() -> Result<HashMap<IfIndex, Interface>, Error> {
+ let mut ifs = HashMap::new();
+ // Microsoft recommends a 15 KB initial buffer
+ let start_size = 15 * 1024;
+ let mut buf: Vec<u8> = vec![0; start_size];
+ let mut sizepointer: u32 = start_size as u32;
+
+ unsafe {
+ loop {
+ let bufptr = &mut buf[0] as *mut _ as *mut IP_ADAPTER_ADDRESSES_LH;
+ let res = GetAdaptersAddresses(
+ 0, /* AF_UNSPEC */
+ GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST,
+ None,
+ Some(bufptr),
+ &mut sizepointer,
+ );
+ match WIN32_ERROR(res) {
+ ERROR_SUCCESS => break,
+ ERROR_ADDRESS_NOT_ASSOCIATED => return Err(Error::Internal),
+ ERROR_BUFFER_OVERFLOW => {
+ buf.resize(sizepointer as usize, 0);
+ continue;
+ }
+ ERROR_INVALID_PARAMETER => return Err(Error::Internal),
+ ERROR_NOT_ENOUGH_MEMORY => return Err(Error::Internal),
+ ERROR_NO_DATA => return Ok(HashMap::new()), // there aren't any
+ _ => return Err(Error::Internal), // TODO: Use FormatMessage to get a string
+ }
+ }
+
+ // We have at least one
+ let mut adapter_ptr = &buf[0] as *const _ as *const IP_ADAPTER_ADDRESSES_LH;
+ while !adapter_ptr.is_null() {
+ let adapter = &*adapter_ptr as &IP_ADAPTER_ADDRESSES_LH;
+ let mut hw_addr = String::with_capacity(adapter.PhysicalAddressLength as usize * 3);
+ for i in 0..adapter.PhysicalAddressLength as usize {
+ if i != 0 {
+ write!(hw_addr, ":").map_err(|_| Error::Internal)?;
+ }
+ write!(hw_addr, "{:02X}", adapter.PhysicalAddress[i])
+ .map_err(|_| Error::Internal)?;
+ }
+ let mut ips = vec![];
+ let mut unicast_ptr = adapter.FirstUnicastAddress;
+ while !unicast_ptr.is_null() {
+ let unicast = &*unicast_ptr as &IP_ADAPTER_UNICAST_ADDRESS_LH;
+ let sockaddr = &*unicast.Address.lpSockaddr as &SOCKADDR;
+ let ip = match sockaddr.sa_family {
+ AF_INET => {
+ let sockaddr_in =
+ &*(unicast.Address.lpSockaddr as *const SOCKADDR_IN) as &SOCKADDR_IN;
+ IpAddr::V4(sockaddr_in.sin_addr.into())
+ }
+ AF_INET6 => {
+ let sockaddr_in6 =
+ &*(unicast.Address.lpSockaddr as *const SOCKADDR_IN6) as &SOCKADDR_IN6;
+ IpAddr::V6(sockaddr_in6.sin6_addr.into())
+ }
+ _ => continue,
+ };
+ ips.push(ip);
+ unicast_ptr = unicast.Next;
+ }
+ let ifindex = adapter.Ipv6IfIndex;
+ let name = adapter
+ .FriendlyName
+ .to_string()
+ .unwrap_or_else(|_| "".to_owned());
+ let iface = Interface {
+ index: ifindex,
+ name,
+ hw_addr,
+ ips,
+ };
+ ifs.insert(ifindex, iface);
+ adapter_ptr = adapter.Next;
+ }
+ }
+
+ Ok(ifs)
+}
+
+#[cfg(test)]
+mod test {
+ use super::list_interfaces;
+
+ #[test]
+ fn list() {
+ let ifaces = list_interfaces().unwrap();
+ println!("{:?}", ifaces);
+ }
+}