ndisapi_rs/ndisapi/static_api.rs
1//! # Submodule: Static NDISAPI functions
2//!
3//! This module provides utility functions for interacting with the Windows Registry related
4//! to NDIS Filter driver and network interfaces. It defines constants for various Registry keys,
5//! values, and data types that are used to access and modify settings related to network interfaces.
6//! It also contains an //! implementation of Ndisapi that includes functions for setting and retrieving
7//! Registry values related to NDIS filter driver and network interfaces.
8//!
9
10use windows::{
11 core::{Result, PCWSTR, PWSTR},
12 s, w,
13 Win32::System::Registry::{
14 RegCloseKey, RegEnumKeyExW, RegOpenKeyExW, RegQueryValueExA, RegQueryValueExW,
15 RegSetValueExW, HKEY, HKEY_LOCAL_MACHINE, KEY_READ, KEY_WRITE, REG_DWORD, REG_VALUE_TYPE,
16 },
17};
18
19use super::Ndisapi;
20use std::str;
21
22/// The registry key path for the network control class.
23const REGSTR_NETWORK_CONTROL_CLASS: ::windows::core::PCWSTR =
24 w!(r"SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}");
25
26/// The name of the registry value.
27const REGSTR_VAL_NAME: ::windows::core::PCWSTR = w!("Name");
28
29/// The name of the registry value containing the component ID.
30const REGSTR_COMPONENTID: ::windows::core::PCSTR = s!("ComponentId");
31
32/// The name of the registry value containing the linkage information.
33const REGSTR_LINKAGE: ::windows::core::PCWSTR = w!("Linkage");
34
35/// The name of the registry value containing the export information.
36const REGSTR_EXPORT: ::windows::core::PCSTR = s!("Export");
37
38/// The name of the registry value containing the MTU decrement value.
39const REGSTR_MTU_DECREMENT: ::windows::core::PCWSTR = w!("MTUDecrement");
40
41/// The name of the registry value containing the network adapter startup filter mode value.
42const REGSTR_STARTUP_MODE: ::windows::core::PCWSTR = w!("StartupMode");
43
44/// The name of the registry value containing theintermediate buffer pool size multiplier.
45const REGSTR_POOL_SIZE: ::windows::core::PCWSTR = w!("PoolSize");
46
47/// The component ID for the NDIS WAN IP driver.
48const REGSTR_COMPONENTID_NDISWANIP: &str = "ms_ndiswanip";
49
50/// The component ID for the NDIS WAN IPv6 driver.
51const REGSTR_COMPONENTID_NDISWANIPV6: &str = "ms_ndiswanipv6";
52
53/// The component ID for the NDIS WAN BH driver.
54const REGSTR_COMPONENTID_NDISWANBH: &str = "ms_ndiswanbh";
55
56/// The user-friendly name for the NDIS WAN IP interface.
57const USER_NDISWANIP: &str = "WAN Network Interface (IP)";
58
59/// The user-friendly name for the NDIS WAN BH interface.
60const USER_NDISWANBH: &str = "WAN Network Interface (BH)";
61
62/// The user-friendly name for the NDIS WAN IPv6 interface.
63const USER_NDISWANIPV6: &str = "WAN Network Interface (IPv6)";
64
65impl Ndisapi {
66 /// Determines if a given network interface is an NDISWAN interface.
67 ///
68 /// This function enumerates all subkeys of the registry key `HKLM\SYSTEM\CurrentControlSet\Control\Class\{4D36E972-E325-11CE-BFC1-08002BE10318}`,
69 /// and looks for the specified `component_id` (e.g., "ms_ndiswanip", "ms_ndiswanipv6", "ms_ndiswanbh").
70 /// If a match is found, it checks the linkage subkey and export string to determine if the interface is an NDISWAN interface.
71 ///
72 /// # Arguments
73 ///
74 /// * `adapter_name: impl Into<String>`: The name of the network adapter to check.
75 /// * `component_id: &str`: The component ID to look for in the registry (e.g., "ms_ndiswanip", "ms_ndiswanipv6", "ms_ndiswanbh").
76 ///
77 /// # Returns
78 ///
79 /// * `Result<bool>`: If successful, returns `Ok(true)` if the interface is an NDISWAN interface, `Ok(false)` otherwise.
80 /// If an error occurs, returns an error.
81 fn is_ndiswan_interface(adapter_name: impl Into<String>, component_id: &str) -> Result<bool> {
82 let adapter_name = adapter_name.into();
83 // Handles to registry keys
84 let mut target_key = HKEY::default();
85 let mut connection_key = HKEY::default();
86 let mut linkage_key = HKEY::default();
87
88 let result = unsafe {
89 RegOpenKeyExW(
90 HKEY_LOCAL_MACHINE,
91 REGSTR_NETWORK_CONTROL_CLASS,
92 0,
93 KEY_READ,
94 &mut target_key,
95 )
96 };
97
98 if result.is_err() {
99 return Err(result.into());
100 }
101
102 // Counter for enumerating registry keys
103 let mut index = 0u32;
104
105 // Buffers for storing registry values
106 let mut buffer = vec![0u16; 256];
107 let mut buffer_size = buffer.len() as u32;
108 let mut temp_buffer = vec![0u8; 256];
109 let mut temp_buffer_size = temp_buffer.len() as u32;
110
111 // Set to true if found
112 let mut found = false;
113
114 while !found {
115 let result = unsafe {
116 RegEnumKeyExW(
117 target_key,
118 index,
119 PWSTR::from_raw(buffer.as_mut_ptr()),
120 &mut buffer_size as *mut u32,
121 None,
122 PWSTR::null(),
123 None,
124 None,
125 )
126 };
127
128 if !result.is_ok() {
129 break;
130 } else {
131 let result = unsafe {
132 RegOpenKeyExW(
133 target_key,
134 PCWSTR::from_raw(buffer.as_ptr()),
135 0,
136 KEY_READ,
137 &mut connection_key,
138 )
139 };
140
141 if result.is_ok() {
142 let mut value_type = REG_VALUE_TYPE::default();
143 let result = unsafe {
144 RegQueryValueExA(
145 connection_key,
146 REGSTR_COMPONENTID,
147 None,
148 Some(&mut value_type),
149 Some(temp_buffer.as_mut_ptr()),
150 Some(&mut temp_buffer_size),
151 )
152 };
153
154 if result.is_ok() {
155 let comp_id = if let Ok(id) =
156 str::from_utf8(&temp_buffer[..temp_buffer_size as usize])
157 {
158 id.trim_end_matches(char::from(0)).to_string()
159 } else {
160 String::default()
161 };
162
163 if comp_id.as_str() == component_id {
164 temp_buffer_size = temp_buffer.len() as u32;
165 let result = unsafe {
166 RegOpenKeyExW(
167 connection_key,
168 REGSTR_LINKAGE,
169 0,
170 KEY_READ,
171 &mut linkage_key,
172 )
173 };
174
175 if result.is_ok() {
176 let result = unsafe {
177 RegQueryValueExA(
178 linkage_key,
179 REGSTR_EXPORT,
180 None,
181 Some(&mut value_type),
182 Some(temp_buffer.as_mut_ptr()),
183 Some(&mut temp_buffer_size),
184 )
185 };
186
187 if result.is_ok() {
188 let export = if let Ok(id) =
189 str::from_utf8(&temp_buffer[..temp_buffer_size as usize])
190 {
191 id.trim_end_matches(char::from(0)).to_string()
192 } else {
193 String::default()
194 };
195
196 if export.as_str().eq_ignore_ascii_case(adapter_name.as_str()) {
197 found = true;
198 }
199 }
200 unsafe {
201 RegCloseKey(linkage_key);
202 }
203 }
204 }
205 unsafe {
206 RegCloseKey(connection_key);
207 }
208 }
209 temp_buffer_size = temp_buffer.len() as u32;
210 }
211
212 index += 1;
213 buffer_size = buffer.len() as u32;
214 }
215 }
216
217 unsafe {
218 RegCloseKey(target_key);
219 }
220
221 Ok(found)
222 }
223
224 /// Determines if a given network interface is an NDISWANIP interface.
225 ///
226 /// This function checks if the specified network adapter is an NDISWANIP interface by calling `is_ndiswan_interface`
227 /// with the component ID "ms_ndiswanip".
228 ///
229 /// # Arguments
230 ///
231 /// * `adapter_name: impl Into<String>`: The name of the network adapter to check.
232 ///
233 /// # Returns
234 ///
235 /// * `bool`: Returns `true` if the interface is an NDISWANIP interface, `false` otherwise.
236 pub fn is_ndiswan_ip(adapter_name: impl Into<String>) -> bool {
237 Self::is_ndiswan_interface(adapter_name.into(), REGSTR_COMPONENTID_NDISWANIP)
238 .unwrap_or(false)
239 }
240
241 /// Determines if a given network interface is an NDISWANIPV6 interface.
242 ///
243 /// This function checks if the specified network adapter is an NDISWANIPV6 interface by calling `is_ndiswan_interface`
244 /// with the component ID "ms_ndiswanipv6".
245 ///
246 /// # Arguments
247 ///
248 /// * `adapter_name: impl Into<String>`: The name of the network adapter to check.
249 ///
250 /// # Returns
251 ///
252 /// * `bool`: Returns `true` if the interface is an NDISWANIPV6 interface, `false` otherwise.
253 pub fn is_ndiswan_ipv6(adapter_name: impl Into<String>) -> bool {
254 Self::is_ndiswan_interface(adapter_name.into(), REGSTR_COMPONENTID_NDISWANIPV6)
255 .unwrap_or(false)
256 }
257
258 /// Determines if a given network interface is an NDISWANBH interface.
259 ///
260 /// This function checks if the specified network adapter is an NDISWANBH interface by calling `is_ndiswan_interface`
261 /// with the component ID "ms_ndiswanbh".
262 ///
263 /// # Arguments
264 ///
265 /// * `adapter_name: impl Into<String>`: The name of the network adapter to check.
266 ///
267 /// # Returns
268 ///
269 /// * `bool`: Returns `true` if the interface is an NDISWANBH interface, `false` otherwise.
270 pub fn is_ndiswan_bh(adapter_name: impl Into<String>) -> bool {
271 Self::is_ndiswan_interface(adapter_name.into(), REGSTR_COMPONENTID_NDISWANBH)
272 .unwrap_or(false)
273 }
274
275 /// This function checks if the specified network adapter is an NDISWAN IP, IPv6, or BH interface, and if not,
276 /// attempts to find the friendly name from the registry.
277 ///
278 /// # Arguments
279 ///
280 /// * `adapter_name: impl Into<String>`: The system-level name of the network adapter to obtain the user-friendly name for.
281 ///
282 /// # Returns
283 ///
284 /// * `Result<String>`: Returns a `Result` containing the user-friendly name of the network adapter if found, or an error otherwise.
285
286 pub fn get_friendly_adapter_name(adapter_name: impl Into<String>) -> Result<String> {
287 let mut adapter_name = adapter_name.into();
288
289 if Self::is_ndiswan_ip(adapter_name.as_str()) {
290 return Ok(USER_NDISWANIP.into());
291 }
292
293 if Self::is_ndiswan_ipv6(adapter_name.as_str()) {
294 return Ok(USER_NDISWANIPV6.into());
295 }
296
297 if Self::is_ndiswan_bh(adapter_name.as_str()) {
298 return Ok(USER_NDISWANBH.into());
299 }
300
301 // Trim the '\DEVICE\' prefix from the adapter system name
302 adapter_name = adapter_name.replace("\\DEVICE\\", "");
303
304 let friendly_name_key = format!(
305 "SYSTEM\\CurrentControlSet\\Control\\Network\\{{4D36E972-E325-11CE-BFC1-08002BE10318}}\\{}\\Connection",
306 &adapter_name
307 );
308
309 // Convert the string to UTF16 array and get a pointer to it as PCWSTR
310 let mut friendly_name_key = friendly_name_key.encode_utf16().collect::<Vec<u16>>();
311 friendly_name_key.push(0);
312
313 let mut hkey = HKEY::default();
314
315 let mut result = unsafe {
316 RegOpenKeyExW(
317 HKEY_LOCAL_MACHINE,
318 PCWSTR::from_raw(friendly_name_key.as_ptr()),
319 0,
320 KEY_READ,
321 &mut hkey,
322 )
323 };
324
325 let mut value_type = REG_VALUE_TYPE::default();
326 let mut data = vec![0u16; 256];
327 let mut data_size = data.len() as u32;
328 let mut friendly_name = String::default();
329
330 if result.is_ok() {
331 result = unsafe {
332 RegQueryValueExW(
333 hkey,
334 REGSTR_VAL_NAME,
335 None,
336 Some(&mut value_type),
337 Some(data.as_mut_ptr() as *const u8 as *mut u8),
338 Some(&mut data_size),
339 )
340 };
341
342 if result.is_ok() {
343 friendly_name = if let Ok(name) = String::from_utf16(&data[..data_size as usize]) {
344 name.trim_end_matches(char::from(0)).to_string()
345 } else {
346 String::default()
347 }
348 }
349
350 unsafe {
351 RegCloseKey(hkey);
352 }
353 }
354
355 if !result.is_ok() {
356 Err(result.into())
357 } else {
358 Ok(friendly_name)
359 }
360 }
361
362 /// This function sets a parameter in the registry key that the filter driver reads during its initialization.
363 /// The value set in the registry is subtracted from the actual MTU (Maximum Transmission Unit) when it is requested
364 /// by the MSTCP (Microsoft TCP/IP) from the network. Because this parameter is read during the initialization of the
365 /// filter driver, a system reboot is required for the changes to take effect. Requires Administrator permissions.
366 ///
367 /// # Arguments
368 ///
369 /// * `mtu_decrement: u32` - The value to subtract from the actual MTU.
370 ///
371 /// # Returns
372 ///
373 /// * `Result<()>` - Returns a `Result` that is `Ok(())` if the MTU decrement value is set successfully in the registry, or an error otherwise.
374 pub fn set_mtu_decrement(&self, mtu_decrement: u32) -> Result<()> {
375 let mut hkey = HKEY::default();
376
377 let mut result = unsafe {
378 RegOpenKeyExW(
379 HKEY_LOCAL_MACHINE,
380 self.get_driver_registry_key(),
381 0,
382 KEY_WRITE,
383 &mut hkey,
384 )
385 };
386
387 if result.is_ok() {
388 result = unsafe {
389 RegSetValueExW(
390 hkey,
391 REGSTR_MTU_DECREMENT,
392 0,
393 REG_DWORD,
394 Some(mtu_decrement.to_ne_bytes().as_ref()),
395 )
396 };
397 }
398
399 if result.is_ok() {
400 Ok(())
401 } else {
402 Err(result.into())
403 }
404 }
405
406 /// This function retrieves the value set by `set_mtu_decrement` from the registry. Note that if you have not
407 /// rebooted after calling `set_mtu_decrement`, the return value is meaningless. If `MTUDecrement` value is not
408 /// present in the registry or an error occurred, then `None` is returned.
409 ///
410 /// # Returns
411 ///
412 /// * `Option<u32>` - Returns an `Option` containing the MTU decrement value if it is present in the registry and there are no errors, or `None` otherwise.
413 pub fn get_mtu_decrement(&self) -> Option<u32> {
414 let mut hkey = HKEY::default();
415
416 let mut result = unsafe {
417 RegOpenKeyExW(
418 HKEY_LOCAL_MACHINE,
419 self.get_driver_registry_key(),
420 0,
421 KEY_READ,
422 &mut hkey,
423 )
424 };
425
426 let mut value_type = REG_VALUE_TYPE::default();
427 let mtu_decrement = 0u32;
428 let mut data_size = std::mem::size_of::<u32>() as u32;
429
430 if result.is_ok() {
431 result = unsafe {
432 RegQueryValueExW(
433 hkey,
434 REGSTR_MTU_DECREMENT,
435 None,
436 Some(&mut value_type),
437 Some(&mtu_decrement as *const u32 as *mut u8),
438 Some(&mut data_size),
439 )
440 };
441 }
442
443 if result.is_ok() {
444 Some(mtu_decrement)
445 } else {
446 None
447 }
448 }
449
450 /// This routine sets the default mode to be applied to each adapter as soon as it appears in the system.
451 /// It can be helpful in scenarios where you need to delay a network interface from operating until your
452 /// application has started. However, it's essential to note that this API call requires a system reboot to take effect.
453 /// Requires Administrator permissions to succeed.
454 ///
455 /// # Arguments
456 ///
457 /// * `startup_mode: u32` - The default startup mode to be applied to each adapter.
458 ///
459 /// # Returns
460 ///
461 /// * `Result<()>` - Returns a `Result` indicating whether the operation succeeded or an error occurred.
462 pub fn set_adapters_startup_mode(&self, startup_mode: u32) -> Result<()> {
463 let mut hkey = HKEY::default();
464
465 let mut result = unsafe {
466 RegOpenKeyExW(
467 HKEY_LOCAL_MACHINE,
468 self.get_driver_registry_key(),
469 0,
470 KEY_WRITE,
471 &mut hkey,
472 )
473 };
474
475 if result.is_ok() {
476 result = unsafe {
477 RegSetValueExW(
478 hkey,
479 REGSTR_STARTUP_MODE,
480 0,
481 REG_DWORD,
482 Some(startup_mode.to_ne_bytes().as_ref()),
483 )
484 };
485 }
486
487 if result.is_ok() {
488 Ok(())
489 } else {
490 Err(result.into())
491 }
492 }
493
494 /// Returns the current default filter mode value applied to each adapter when it appears in the system.
495 /// Note that if you have not rebooted after calling SetAdaptersStartupMode, the return value is meaningless.
496 ///
497 /// # Returns
498 ///
499 /// * `Option<u32>` - Returns the current default startup mode as `Some(u32)` if the value is present in the registry,
500 /// or `None` if the value is not present or an error occurred.
501 pub fn get_adapters_startup_mode(&self) -> Option<u32> {
502 let mut hkey = HKEY::default();
503
504 let mut result = unsafe {
505 RegOpenKeyExW(
506 HKEY_LOCAL_MACHINE,
507 self.get_driver_registry_key(),
508 0,
509 KEY_READ,
510 &mut hkey,
511 )
512 };
513
514 let mut value_type = REG_VALUE_TYPE::default();
515 let startup_mode = 0u32;
516 let mut data_size = std::mem::size_of::<u32>() as u32;
517
518 if result.is_ok() {
519 result = unsafe {
520 RegQueryValueExW(
521 hkey,
522 REGSTR_STARTUP_MODE,
523 None,
524 Some(&mut value_type),
525 Some(&startup_mode as *const u32 as *mut u8),
526 Some(&mut data_size),
527 )
528 };
529 }
530
531 if result.is_ok() {
532 Some(startup_mode)
533 } else {
534 None
535 }
536 }
537
538 /// Sets the pool size multiplier for Windows Packet Filter driver in the Windows registry.
539 ///
540 /// This function creates or modifies the PoolSize value in the registry based on the
541 /// given value. The appropriate registry key is selected depending on the
542 /// Windows platform (NT/2000/XP or 9x/ME). The resulting internal packet pool size
543 /// will be equal to 2048 (512 for Windows version before Vista) * PoolSize packets. The maximum
544 /// effective PoolSize is 10.
545 ///
546 /// # Arguments
547 ///
548 /// * `pool_size: u32` - The desired pool size multiplier to be set in the registry.
549 ///
550 /// # Returns
551 ///
552 /// * `Result<()>` - If the pool size multiplier is successfully set, returns `Ok(())`.
553 /// Otherwise, returns an `Err` with the error code.
554 pub fn set_pool_size(&self, pool_size: u32) -> Result<()> {
555 let mut hkey = HKEY::default();
556
557 let mut result = unsafe {
558 RegOpenKeyExW(
559 HKEY_LOCAL_MACHINE,
560 self.get_driver_registry_key(),
561 0,
562 KEY_WRITE,
563 &mut hkey,
564 )
565 };
566
567 if result.is_ok() {
568 result = unsafe {
569 RegSetValueExW(
570 hkey,
571 REGSTR_POOL_SIZE,
572 0,
573 REG_DWORD,
574 Some(pool_size.to_ne_bytes().as_ref()),
575 )
576 };
577 }
578
579 if result.is_ok() {
580 Ok(())
581 } else {
582 Err(result.into())
583 }
584 }
585
586 /// Retrieves the pool size multiplier for the Windows Packet Filter driver from the Windows registry.
587 ///
588 /// This function queries the registry for the PoolSize value and returns it.
589 /// The appropriate registry key is used depending on the Windows platform
590 /// (NT/2000/XP or 9x/ME). The internal packet pool size is determined by
591 /// 2048 * PoolSize packets. The maximum effective PoolSize is 10.
592 ///
593 /// # Returns
594 ///
595 /// * `Option<u32>` - The pool size multiplier retrieved from the registry.
596 /// If the value is not found or an error occurs, returns `None`.
597 pub fn get_pool_size(&self) -> Option<u32> {
598 let mut hkey = HKEY::default();
599
600 let mut result = unsafe {
601 RegOpenKeyExW(
602 HKEY_LOCAL_MACHINE,
603 self.get_driver_registry_key(),
604 0,
605 KEY_READ,
606 &mut hkey,
607 )
608 };
609
610 let mut value_type = REG_VALUE_TYPE::default();
611 let pool_size = 0u32;
612 let mut data_size = std::mem::size_of::<u32>() as u32;
613
614 if result.is_ok() {
615 result = unsafe {
616 RegQueryValueExW(
617 hkey,
618 REGSTR_POOL_SIZE,
619 None,
620 Some(&mut value_type),
621 Some(&pool_size as *const u32 as *mut u8),
622 Some(&mut data_size),
623 )
624 };
625 }
626
627 if result.is_ok() {
628 Some(pool_size)
629 } else {
630 None
631 }
632 }
633}