ndisapi/netlib/ip_helper/network_adapter_info.rs
1use crate::{GuidWrapper, IfLuid, IpGatewayInfo, MacAddress, SockAddrStorage};
2use std::net::IpAddr;
3use windows::{
4 core::{w, Result, PCWSTR},
5 Win32::{
6 NetworkManagement::{
7 IpHelper::{
8 ResolveIpNetEntry2, IF_TYPE_ETHERNET_CSMACD, IF_TYPE_IEEE80211,
9 IP_ADAPTER_ADDRESSES_LH, MIB_IF_ROW2, MIB_IPNET_ROW2,
10 },
11 Ndis::{NDIS_MEDIUM, NDIS_PHYSICAL_MEDIUM},
12 },
13 Networking::WinSock::{AF_INET, AF_INET6},
14 System::Registry::{
15 RegCloseKey, RegOpenKeyExW, RegSetValueExW, HKEY, HKEY_LOCAL_MACHINE, KEY_WRITE, REG_SZ,
16 },
17 },
18};
19
20// Submodules
21pub mod address;
22pub mod gateway;
23pub mod getters;
24pub mod ndp;
25pub mod routing;
26pub mod statics;
27pub mod util;
28
29/// Represents information about a network adapter.
30///
31/// This struct provides an easy-to-use and safe interface for working with network adapter
32/// information, such as IP addresses, DNS server addresses, gateway addresses, and more.
33#[derive(Debug, Clone)]
34pub struct IphlpNetworkAdapterInfo {
35 /// The index of the IPv4 interface.
36 if_index: u32,
37 /// The interface index for the IPv6 IP address. This member is zero if IPv6 is not available on the interface.
38 ipv6_if_index: u32,
39 /// Contains the name of the adapter. Unlike an adapter's friendly name, the adapter name specified in `adapter_name_` is permanent and cannot be modified by the user.
40 adapter_name: String,
41 /// Contains the name of the underlying hardware adapter.
42 true_adapter_name: GuidWrapper,
43 /// A description for the adapter.
44 description: String,
45 /// A user-friendly name for the adapter.
46 friendly_name: String,
47 /// List of IP unicast addresses for the adapter.
48 unicast_address_list: Vec<IpAddr>,
49 /// List of DNS server addresses for the adapter.
50 dns_server_address_list: Vec<IpAddr>,
51 /// List of gateways for the adapter.
52 gateway_address_list: Vec<IpGatewayInfo>,
53 /// The Media Access Control (MAC) address for the adapter.
54 physical_address: MacAddress,
55 /// The maximum transmission unit (MTU) size, in bytes.
56 mtu: u16,
57 /// The interface type as defined by the Internet Assigned Names Authority (IANA).
58 /// Possible values for the interface type are listed in the `Ipifcons.h` header file.
59 if_type: u32,
60 /// The current speed in bits per second of the transmit link for the adapter.
61 transmit_link_speed: u64,
62 /// The current speed in bits per second of the receive link for the adapter.
63 receive_link_speed: u64,
64 /// The interface LUID for the adapter address.
65 luid: IfLuid,
66 /// The NDIS media type for the interface. This member can be one of the values from the `NDIS_MEDIUM`
67 /// enumeration type defined in the `Ntddndis.h` header file.
68 media_type: NDIS_MEDIUM,
69 /// The NDIS physical medium type. This member can be one of the values from the `NDIS_PHYSICAL_MEDIUM`
70 /// enumeration type defined in the `Ntddndis.h` header file.
71 physical_medium_type: NDIS_PHYSICAL_MEDIUM,
72 /// If `physical_medium_type_` is `NdisPhysicalMediumUnspecified` (virtual network interface on top of the real one),
73 /// this one may contain real physical media.
74 true_medium_type: NDIS_PHYSICAL_MEDIUM,
75 /// NDISWANIP associated MAC address.
76 ndis_wan_ip_link: MacAddress,
77 /// NDISWANIPV6 associated MAC address.
78 ndis_wan_ipv6_link: MacAddress,
79}
80
81impl IphlpNetworkAdapterInfo {
82 /// Constructs a new `IphlpNetworkAdapterInfo` instance from raw pointers to `IP_ADAPTER_ADDRESSES_LH`
83 /// and `MIB_IF_ROW2` structures.
84 ///
85 /// This method is marked as `unsafe` because it dereferences raw pointers, which can lead
86 /// to undefined behavior if not used correctly.
87 ///
88 /// # Safety
89 ///
90 /// The caller must ensure that `address` and `if_row` are valid pointers to
91 /// `IP_ADAPTER_ADDRESSES_LH` and `MIB_IF_ROW2` structures, respectively, and that the
92 /// structures remain valid for the duration of the method call.
93 ///
94 /// # Arguments
95 ///
96 /// * `address` - A pointer to an `IP_ADAPTER_ADDRESSES_LH` structure containing information
97 /// about the network adapter.
98 /// * `if_row` - A pointer to a `MIB_IF_ROW2` structure containing information about the
99 /// network interface.
100 ///
101 /// # Examples
102 ///
103 /// ```rust,ignore
104 /// use my_crate::IphlpNetworkAdapterInfo;
105 /// use my_crate::{IP_ADAPTER_ADDRESSES_LH, MIB_IF_ROW2};
106 ///
107 /// unsafe {
108 /// let address = /* ... */;
109 /// let if_row = /* ... */;
110 ///
111 /// let network_adapter_info = IphlpNetworkAdapterInfo::new(address, if_row);
112 /// }
113 ///
114 pub unsafe fn new(address: *const IP_ADAPTER_ADDRESSES_LH, if_row: *const MIB_IF_ROW2) -> Self {
115 let address = unsafe { &*address };
116
117 let mut unicast_address_list = Vec::new();
118 let mut unicast_address = address.FirstUnicastAddress;
119 while !unicast_address.is_null() {
120 unicast_address_list.push(
121 SockAddrStorage::from_sockaddr(unsafe { *(*unicast_address).Address.lpSockaddr })
122 .into(),
123 );
124 unicast_address = unsafe { (*unicast_address).Next };
125 }
126
127 let mut dns_server_address_list = Vec::new();
128 let mut dns_address = address.FirstDnsServerAddress;
129 while !dns_address.is_null() {
130 dns_server_address_list.push(
131 SockAddrStorage::from_sockaddr(unsafe { *(*dns_address).Address.lpSockaddr })
132 .into(),
133 );
134 dns_address = unsafe { (*dns_address).Next };
135 }
136
137 let mut gateway_address_list = Vec::new();
138 let mut gateway_address = address.FirstGatewayAddress;
139 while !gateway_address.is_null() {
140 gateway_address_list.push(IpGatewayInfo::new(
141 SockAddrStorage::from_sockaddr(unsafe { *(*gateway_address).Address.lpSockaddr })
142 .into(),
143 None,
144 ));
145 gateway_address = unsafe { (*gateway_address).Next };
146 }
147
148 let mut result = Self {
149 if_index: unsafe { address.Anonymous1.Anonymous.IfIndex },
150 ipv6_if_index: address.Ipv6IfIndex,
151 adapter_name: unsafe { address.AdapterName.to_string() }.unwrap_or_default(),
152 description: unsafe { address.Description.to_string() }.unwrap_or_default(),
153 friendly_name: unsafe { address.FriendlyName.to_string() }.unwrap_or_default(),
154 physical_address: MacAddress::from_slice(&address.PhysicalAddress).unwrap_or_default(),
155 mtu: address.Mtu as u16,
156 if_type: address.IfType,
157 transmit_link_speed: address.TransmitLinkSpeed,
158 receive_link_speed: address.ReceiveLinkSpeed,
159 luid: IfLuid::new(&address.Luid),
160 media_type: (*if_row).MediaType,
161 physical_medium_type: (*if_row).PhysicalMediumType,
162 unicast_address_list,
163 dns_server_address_list,
164 gateway_address_list,
165 true_adapter_name: GuidWrapper::new(),
166 true_medium_type: NDIS_PHYSICAL_MEDIUM::default(),
167 ndis_wan_ip_link: MacAddress::default(),
168 ndis_wan_ipv6_link: MacAddress::default(),
169 };
170
171 // For each ethernet of wi-fi interface initialize the gateway hardware address list by resolving
172 // the hardware addresses for each gateway IP address in the list.
173 if address.IfType == IF_TYPE_ETHERNET_CSMACD || address.IfType == IF_TYPE_IEEE80211 {
174 result.initialize_gateway_hw_address_list();
175 }
176
177 result
178 }
179
180 /// Initializes the gateway hardware address list by resolving the hardware addresses
181 /// for each gateway IP address in the list.
182 ///
183 /// This function iterates over the `gateway_address_list` and resolves the hardware addresses
184 /// by updating the `MIB_IPNET_ROW2` structure and calling the `ResolveIpNetEntry2` function.
185 /// If successful, the hardware address is assigned to the `hardware_address` field of the
186 /// corresponding `Address` structure.
187 ///
188 /// # Safety
189 ///
190 /// This function contains unsafe code blocks due to the use of FFI calls to the WinAPI, direct memory access,
191 /// and pointer casting. Make sure that the data structures are properly initialized and that the
192 /// WinAPI functions are used correctly to ensure memory safety.
193 ///
194 unsafe fn initialize_gateway_hw_address_list(&mut self) {
195 if !self.gateway_address_list.is_empty() {
196 // check if the address list is empty
197 for address in &mut self.gateway_address_list {
198 // iterate through each address in the list
199 let mut row: MIB_IPNET_ROW2 = unsafe { std::mem::zeroed() }; // create a new row object with default values
200
201 // assign values to new row object
202 row.Address.si_family = if address.ip_address.is_ipv4() {
203 AF_INET
204 } else {
205 AF_INET6
206 }; // assign si_family value
207 row.InterfaceLuid = self.luid.into(); // assign InterfaceLuid value
208
209 // match against address family and assign to proper field in row
210 match address.ip_address {
211 IpAddr::V4(ip_address) => {
212 // for IPv4 addresses
213 row.Address.Ipv4.sin_addr = ip_address.into();
214 }
215 IpAddr::V6(ip_address) => {
216 // for IPv6 addresses
217 row.Address.Ipv6.sin6_addr = ip_address.into();
218 }
219 }
220
221 // resolve IP address to MAC address using the current row and store it in the address object
222 if unsafe { ResolveIpNetEntry2(&mut row, None) }.is_ok() {
223 address.hardware_address =
224 MacAddress::from_slice(&row.PhysicalAddress).unwrap_or_default();
225 }
226 }
227 }
228 }
229
230 /// Sets the friendly name of the network adapter.
231 ///
232 /// This function sets the friendly name of the network adapter by updating the Windows registry.
233 /// The registry key for the adapter is located at:
234 /// `SYSTEM\CurrentControlSet\Control\Network\{4D36E972-E325-11CE-BFC1-08002BE10318}\{adapter_name}\Connection`.
235 ///
236 /// # Arguments
237 ///
238 /// * `friendly_name` - A string or any type that can be converted into a `String` representing the friendly name of the adapter.
239 ///
240 /// # Returns
241 ///
242 /// * `Result<()>` - Returns `Ok(())` if the friendly name is set successfully. Returns an `Err` containing the error code if there is a failure.
243 ///
244 // This function sets the friendly name of a network adapter and updates the registry accordingly
245 pub fn set_friendly_name(&mut self, friendly_name: impl Into<String>) -> Result<()> {
246 self.friendly_name = friendly_name.into(); // set the friendly name of the adapter to the input value
247
248 // create the registry key path for the adapter's connection settings
249 let friendly_name_key = format!(
250 "SYSTEM\\CurrentControlSet\\Control\\Network\\{{4D36E972-E325-11CE-BFC1-08002BE10318}}\\{}\\Connection",
251 &self.adapter_name
252 );
253
254 // Convert the string to UTF16 array and get a pointer to it as PCWSTR
255 let mut friendly_name_key = friendly_name_key.encode_utf16().collect::<Vec<u16>>();
256 friendly_name_key.push(0); // add null terminator to end of key string
257
258 let mut hkey = HKEY::default();
259
260 // open the registry key for the adapter's connection settings with write access
261 let mut result = unsafe {
262 RegOpenKeyExW(
263 HKEY_LOCAL_MACHINE, // handle to a pre-defined registry key
264 PCWSTR::from_raw(friendly_name_key.as_ptr()), // registry key path as a PCWSTR pointer
265 0, // reserved (ignored)
266 KEY_WRITE, // desired security access level
267 &mut hkey, // pointer to a variable to receive the handle to the opened key
268 )
269 };
270
271 if result.is_ok() {
272 // if the key was successfully opened
273 // set the AdapterName registry value to the friendly name of the adapter
274 result = unsafe {
275 RegSetValueExW(
276 hkey, // handle to an open registry key
277 w!("Name"), // name of the value to be set
278 0, // reserved (ignored)
279 REG_SZ, // data type of the value
280 Some(self.friendly_name.as_bytes()), // pointer to the buffer containing the value's data
281 )
282 };
283
284 _ = unsafe {
285 RegCloseKey(hkey) // close the registry key handle
286 };
287 }
288
289 result.ok()
290 }
291
292 /// Checks if IP address information in the provided network_adapter_info is different
293 /// from the current one.
294 ///
295 /// # Arguments
296 ///
297 /// * `rhs`: IphlpNetworkAdapterInfo to compare to
298 /// * `check_gateway`: If true, also checks the gateway information
299 ///
300 /// # Returns
301 ///
302 /// * `bool`: true if provided IphlpNetworkAdapterInfo contains the same IP addresses, false otherwise
303 pub fn is_same_address_info(&self, rhs: &IphlpNetworkAdapterInfo, check_gateway: bool) -> bool {
304 if self.unicast_address_list.len() != rhs.unicast_address_list.len() {
305 return false;
306 }
307
308 if check_gateway && self.gateway_address_list.len() != rhs.gateway_address_list.len() {
309 return false;
310 }
311
312 // Check if any of the unicast addresses have changed
313 let ret_val = rhs
314 .unicast_address_list
315 .iter()
316 .all(|address| self.unicast_address_list.contains(address));
317
318 if !ret_val {
319 return ret_val;
320 }
321
322 // Check if any of the gateways have changed
323 if check_gateway {
324 rhs.gateway_address_list
325 .iter()
326 .all(|address| self.gateway_address_list.contains(address))
327 } else {
328 ret_val
329 }
330 }
331
332 /// Resets the adapter's addresses and routes.
333 ///
334 /// # Returns
335 ///
336 /// * `bool`: true if successful, false otherwise.
337 pub fn reset_adapter(&self) -> bool {
338 self.reset_unicast_addresses() && self.reset_adapter_routes()
339 }
340}