]> code.octet-stream.net Git - netwatcher/blob - src/list_win.rs
Clean up race condition in initial watch on Windows
[netwatcher] / src / list_win.rs
1 use std::collections::HashMap;
2 use std::fmt::Write;
3 use std::net::IpAddr;
4
5 use windows::Win32::Foundation::{
6 ERROR_ADDRESS_NOT_ASSOCIATED, ERROR_BUFFER_OVERFLOW, ERROR_INVALID_PARAMETER,
7 ERROR_NOT_ENOUGH_MEMORY, ERROR_NO_DATA, ERROR_SUCCESS, WIN32_ERROR,
8 };
9 use windows::Win32::NetworkManagement::IpHelper::{
10 GetAdaptersAddresses, IP_ADAPTER_UNICAST_ADDRESS_LH,
11 };
12 use windows::Win32::NetworkManagement::IpHelper::{
13 GAA_FLAG_SKIP_ANYCAST, GAA_FLAG_SKIP_MULTICAST, IP_ADAPTER_ADDRESSES_LH,
14 };
15 use windows::Win32::Networking::WinSock::{
16 AF_INET, AF_INET6, AF_UNSPEC, SOCKADDR, SOCKADDR_IN, SOCKADDR_IN6,
17 };
18
19 use crate::{Error, Interface, List};
20
21 pub(crate) fn list_interfaces() -> Result<List, Error> {
22 let mut ifs = HashMap::new();
23 // Microsoft recommends a 15 KB initial buffer
24 let start_size = 15 * 1024;
25 let mut buf: Vec<u8> = vec![0; start_size];
26 let mut sizepointer: u32 = start_size as u32;
27
28 unsafe {
29 loop {
30 let bufptr = &mut buf[0] as *mut _ as *mut IP_ADAPTER_ADDRESSES_LH;
31 let res = GetAdaptersAddresses(
32 AF_UNSPEC.0.into(),
33 GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST,
34 None,
35 Some(bufptr),
36 &mut sizepointer,
37 );
38 match WIN32_ERROR(res) {
39 ERROR_SUCCESS => break,
40 ERROR_ADDRESS_NOT_ASSOCIATED => return Err(Error::Internal),
41 ERROR_BUFFER_OVERFLOW => {
42 buf.resize(sizepointer as usize, 0);
43 continue;
44 }
45 ERROR_INVALID_PARAMETER => return Err(Error::Internal),
46 ERROR_NOT_ENOUGH_MEMORY => return Err(Error::Internal),
47 ERROR_NO_DATA => return Ok(List(HashMap::new())), // there aren't any
48 _ => return Err(Error::Internal), // TODO: Use FormatMessage to get a string
49 }
50 }
51
52 // We have at least one
53 let mut adapter_ptr = &buf[0] as *const _ as *const IP_ADAPTER_ADDRESSES_LH;
54 while !adapter_ptr.is_null() {
55 let adapter = &*adapter_ptr as &IP_ADAPTER_ADDRESSES_LH;
56 let mut hw_addr = String::with_capacity(adapter.PhysicalAddressLength as usize * 3);
57 for i in 0..adapter.PhysicalAddressLength as usize {
58 if i != 0 {
59 write!(hw_addr, ":").map_err(|_| Error::Internal)?;
60 }
61 write!(hw_addr, "{:02X}", adapter.PhysicalAddress[i])
62 .map_err(|_| Error::Internal)?;
63 }
64 let mut ips = vec![];
65 let mut unicast_ptr = adapter.FirstUnicastAddress;
66 while !unicast_ptr.is_null() {
67 let unicast = &*unicast_ptr as &IP_ADAPTER_UNICAST_ADDRESS_LH;
68 let sockaddr = &*unicast.Address.lpSockaddr as &SOCKADDR;
69 let ip = match sockaddr.sa_family {
70 AF_INET => {
71 let sockaddr_in =
72 &*(unicast.Address.lpSockaddr as *const SOCKADDR_IN) as &SOCKADDR_IN;
73 IpAddr::V4(sockaddr_in.sin_addr.into())
74 }
75 AF_INET6 => {
76 let sockaddr_in6 =
77 &*(unicast.Address.lpSockaddr as *const SOCKADDR_IN6) as &SOCKADDR_IN6;
78 IpAddr::V6(sockaddr_in6.sin6_addr.into())
79 }
80 _ => continue,
81 };
82 ips.push(ip);
83 unicast_ptr = unicast.Next;
84 }
85 let ifindex = adapter.Ipv6IfIndex;
86 let name = adapter
87 .FriendlyName
88 .to_string()
89 .unwrap_or_else(|_| "".to_owned());
90 let iface = Interface {
91 index: ifindex,
92 name,
93 hw_addr,
94 ips,
95 };
96 ifs.insert(ifindex, iface);
97 adapter_ptr = adapter.Next;
98 }
99 }
100
101 Ok(List(ifs))
102 }
103
104 #[cfg(test)]
105 mod test {
106 use super::list_interfaces;
107
108 #[test]
109 fn list() {
110 let ifaces = list_interfaces().unwrap().0;
111 println!("{:?}", ifaces);
112 }
113 }