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}