1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
//! # Submodule: Basic NDISAPI functions
//!
//! This submodule provides a comprehensive set of functionalities for interacting with the Windows Packet Filter Kit,
//! allowing users to perform various actions on network adapters within Windows operating systems.
//! It includes methods for setting various adapter parameters, configuring packet filter modes,
//! handling hardware packet filters, and managing events related to adapter list changes
//! and WAN connections.

use std::mem::{size_of, MaybeUninit};

use windows::{
    core::Result,
    Win32::Foundation::{GetLastError, HANDLE},
    Win32::System::IO::DeviceIoControl,
};

use super::Ndisapi;
use crate::driver::*;
use crate::ndisapi::defs::*;

pub const OID_GEN_CURRENT_PACKET_FILTER: u32 = 0x0001010E;

impl Ndisapi {
    /// This method takes an adapter handle as an argument and returns a Result containing
    /// the FilterFlags enum for the selected network interface. If an error occurs, the
    /// GetLastError function is called to retrieve the error and is then converted into
    /// a Result::Err variant.
    ///
    /// # Arguments
    ///
    /// * `adapter_handle` - A HANDLE representing the network interface for which the
    ///   packet filter mode should be queried.
    ///
    /// # Returns
    ///
    /// * `Result<FilterFlags>` - A Result containing the FilterFlags enum for the selected
    ///   network interface if the query was successful, or an error if it failed.
    pub fn get_adapter_mode(&self, adapter_handle: HANDLE) -> Result<FilterFlags> {
        let mut adapter_mode = AdapterMode {
            adapter_handle,
            ..Default::default()
        };

        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_GET_ADAPTER_MODE,
                Some(&adapter_mode as *const AdapterMode as *const std::ffi::c_void),
                size_of::<AdapterMode>() as u32,
                Some(&mut adapter_mode as *mut AdapterMode as *mut std::ffi::c_void),
                size_of::<AdapterMode>() as u32,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(adapter_mode.flags)
        }
    }

    /// This method takes an adapter handle as an argument and returns a Result containing
    /// a u32 value representing the hardware packet filter for the specified network interface.
    /// If an error occurs, it will be propagated as a Result::Err variant.
    ///
    /// # Arguments
    ///
    /// * `adapter_handle` - A HANDLE representing the network interface for which the
    ///   hardware packet filter should be queried.
    ///
    /// # Returns
    ///
    /// * `Result<u32>` - A Result containing a u32 value representing the hardware packet
    ///   filter for the specified network interface if the query was successful, or an error
    ///   if it failed.
    pub fn get_hw_packet_filter(&self, adapter_handle: HANDLE) -> Result<u32> {
        let mut oid = PacketOidData::new(adapter_handle, OID_GEN_CURRENT_PACKET_FILTER, 0u32);

        self.ndis_get_request::<_>(&mut oid)?;

        Ok(oid.data)
    }

    /// This method takes an adapter handle and a mutable reference to a RasLinks struct
    /// as arguments. It queries the active WAN connections from the NDIS filter driver
    /// and updates the `ras_links` argument with the received information. If an error
    /// occurs, it will be propagated as a Result::Err variant.
    ///
    /// # Arguments
    ///
    /// * `adapter_handle` - A HANDLE representing the network interface for which the
    ///   active WAN connections should be queried.
    /// * `ras_links` - A mutable reference to a RasLinks struct that will be updated
    ///   with the information about active WAN connections.
    ///
    /// # Returns
    ///
    /// * `Result<()>` - A Result containing an empty tuple if the query was successful,
    ///   or an error if it failed.
    pub fn get_ras_links(&self, adapter_handle: HANDLE, ras_links: &mut RasLinks) -> Result<()> {
        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_GET_RAS_LINKS,
                Some(&adapter_handle as *const HANDLE as *const std::ffi::c_void),
                size_of::<HANDLE>() as u32,
                Some(ras_links as *const RasLinks as *mut std::ffi::c_void),
                size_of::<RasLinks>() as u32,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }

    /// This method retrieves information about network interfaces that available to NDIS filter driver.
    /// It returns a Result containing a vector of NetworkAdapterInfo
    /// structs, which contain detailed information about each network interface.
    ///
    /// # Returns
    ///
    /// * `Result<Vec<NetworkAdapterInfo>>` - A Result containing a vector of NetworkAdapterInfo
    /// structs representing the available network interfaces if the query was successful,
    /// or an error if it failed.
    pub fn get_tcpip_bound_adapters_info(&self) -> Result<Vec<NetworkAdapterInfo>> {
        let mut adapters: MaybeUninit<TcpAdapterList> = ::std::mem::MaybeUninit::uninit();

        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_GET_TCPIP_INTERFACES,
                None,
                0,
                Some(adapters.as_mut_ptr() as _),
                size_of::<TcpAdapterList>() as u32,
                None,
                None,
            )
        };

        if result.as_bool() {
            let mut result = Vec::new();
            let adapters = unsafe { adapters.assume_init() };

            for i in 0..adapters.adapter_count as usize {
                let adapter_name =
                    String::from_utf8(adapters.adapter_name_list[i].to_vec()).unwrap();
                let adapter_name = adapter_name.trim_end_matches(char::from(0)).to_owned();
                let next = NetworkAdapterInfo::new(
                    adapter_name,
                    adapters.adapter_handle[i],
                    adapters.adapter_medium_list[i],
                    adapters.current_address[i],
                    adapters.mtu[i],
                );
                result.push(next);
            }
            Ok(result)
        } else {
            Err(unsafe { GetLastError() }.into())
        }
    }

    /// This method retrieves the version of the NDIS filter driver currently running on the
    /// system. It returns a Result containing a Version struct with the major, minor, and
    /// revision numbers of the driver version.
    ///
    /// # Returns
    ///
    /// * `Result<Version>` - A Result containing a Version struct representing the NDIS
    ///   filter driver version if the query was successful, or an error if it failed.
    pub fn get_version(&self) -> Result<Version> {
        let mut version = u32::MAX;

        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_GET_VERSION,
                Some(&mut version as *mut u32 as _),
                size_of::<u32>() as u32,
                Some(&mut version as *mut u32 as _),
                size_of::<u32>() as u32,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(Version {
                major: (version & (0xF000)) >> 12,
                minor: (version & (0xFF000000)) >> 24,
                revision: (version & (0xFF0000)) >> 16,
            })
        }
    }

    /// This function is used to obtain various parameters of the network adapter, such as the
    /// dimension of the internal buffers, the link speed, or the counter of corrupted packets.
    /// The constants that define the operations are declared in the file `ntddndis.h`.
    ///
    /// # Type Parameters
    ///
    /// * `T`: The type of data to be queried from the network adapter.
    ///
    /// # Arguments
    ///
    /// * `oid_request`: A mutable reference to a `PacketOidData<T>` struct that specifies
    ///   the adapter handle and the operation to perform.
    ///
    /// # Returns
    ///
    /// * `Result<()>` - A Result indicating whether the query operation was successful or not.
    ///   On success, returns `Ok(())`. On failure, returns an error.
    pub fn ndis_get_request<T>(&self, oid_request: &mut PacketOidData<T>) -> Result<()> {
        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_NDIS_GET_REQUEST,
                Some(oid_request as *const PacketOidData<T> as *const std::ffi::c_void),
                size_of::<PacketOidData<T>>() as u32,
                Some(oid_request as *const PacketOidData<T> as *mut std::ffi::c_void),
                size_of::<PacketOidData<T>>() as u32,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }

    /// This function is used to set various parameters of the network adapter, such as the
    /// dimension of the internal buffers, the link speed, or the counter of corrupted packets.
    /// The constants that define the operations are declared in the file `ntddndis.h`.
    ///
    /// # Type Parameters
    ///
    /// * `T`: The type of data to be set for the network adapter.
    ///
    /// # Arguments
    ///
    /// * `oid_request`: A reference to a `PacketOidData<T>` struct that specifies
    ///   the adapter handle and the operation to perform.
    ///
    /// # Returns
    ///
    /// * `Result<()>` - A Result indicating whether the set operation was successful or not.
    ///   On success, returns `Ok(())`. On failure, returns an error.
    pub fn ndis_set_request<T>(&self, oid_request: &PacketOidData<T>) -> Result<()> {
        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_NDIS_SET_REQUEST,
                Some(oid_request as *const PacketOidData<T> as *const std::ffi::c_void),
                size_of::<PacketOidData<T>>() as u32,
                None,
                0,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }

    /// The user application should create a Win32 event (with the `CreateEvent` API call) and pass
    /// the event handle to this function. The helper driver will signal this event when the
    /// NDIS filter adapter's list changes, for example, when a network card is plugged/unplugged,
    /// a network connection is disabled/enabled, or other similar events.
    ///
    /// # Arguments
    ///
    /// * `event_handle`: A `HANDLE` to a Win32 event created by the user application.
    ///
    /// # Returns
    ///
    /// * `Result<()>` - A Result indicating whether setting the event was successful or not.
    ///   On success, returns `Ok(())`. On failure, returns an error.
    pub fn set_adapter_list_change_event(&self, event_handle: HANDLE) -> Result<()> {
        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_SET_ADAPTER_EVENT,
                Some(&event_handle as *const HANDLE as *const std::ffi::c_void),
                size_of::<HANDLE>() as u32,
                None,
                0,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }

    /// Sets the packet filter mode for the selected network interface.
    ///
    /// # Arguments
    ///
    /// * `adapter_handle`: A `HANDLE` to the network interface (obtained via call to `get_tcpip_bound_adapters_info`).
    /// * `flags`: A `FilterFlags` value representing the combination of packet filter mode flags.
    /// * `MSTCP_FLAG_SENT_TUNNEL` – Queue all packets sent from MSTCP to the network interface. Original packet dropped.
    /// * `MSTCP_FLAG_RECV_TUNNEL` – Queue all packets indicated by the network interface to MSTCP. Original packet dropped.
    /// * `MSTCP_FLAG_SENT_LISTEN` – Queue all packets sent from MSTCP to the network interface. Original packet goes ahead.
    /// * `MSTCP_FLAG_RECV_LISTEN` – Queue all packets indicated by the network interface to MSTCP. Original packet goes ahead.
    /// * `MSTCP_FLAG_FILTER_DIRECT` – In promiscuous mode, the TCP/IP stack receives all packets in the Ethernet segment and replies
    ///   with various ICMP packets. To prevent this, set this flag. All packets with destination MAC different from
    ///   FF-FF-FF-FF-FF-FF and the network interface's current MAC will never reach MSTCP.
    ///
    /// # Returns
    ///
    /// * `Result<()>` - A Result indicating whether setting the adapter mode was successful or not.
    ///   On success, returns `Ok(())`. On failure, returns an error.
    pub fn set_adapter_mode(&self, adapter_handle: HANDLE, flags: FilterFlags) -> Result<()> {
        let adapter_mode = AdapterMode {
            adapter_handle,
            flags,
        };

        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_SET_ADAPTER_MODE,
                Some(&adapter_mode as *const AdapterMode as *const std::ffi::c_void),
                size_of::<AdapterMode>() as u32,
                None,
                0,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }

    /// This method allows setting the hardware packet filter mode for the specified network interface by calling
    /// the `ndis_set_request` function.
    ///
    /// # Arguments
    ///
    /// * `adapter_handle`: A `HANDLE` to the network interface.
    /// * `filter`: A `u32` value representing the packet filter mode.
    ///
    /// # Returns
    ///
    /// * `Result<()>` - A Result indicating whether setting the hardware packet filter was successful or not.
    ///   On success, returns `Ok(())`. On failure, returns an error.
    pub fn set_hw_packet_filter(&self, adapter_handle: HANDLE, filter: u32) -> Result<()> {
        let oid = PacketOidData::new(adapter_handle, OID_GEN_CURRENT_PACKET_FILTER, filter);

        self.ndis_set_request::<_>(&oid)?;

        Ok(())
    }

    /// This method allows setting a Win32 event that will be signaled by the filter driver when the hardware packet
    /// filter for the network interface changes.
    ///
    /// # Arguments
    ///
    /// * `event_handle`: A `HANDLE` to the Win32 event created by the user application.
    ///
    /// # Returns
    ///
    /// * `Result<()>` - A Result indicating whether setting the hardware packet filter event was successful or not.
    ///   On success, returns `Ok(())`. On failure, returns an error.
    pub fn set_hw_packet_filter_event(&self, event_handle: HANDLE) -> Result<()> {
        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_SET_ADAPTER_HWFILTER_EVENT,
                Some(&event_handle as *const HANDLE as *const std::ffi::c_void),
                size_of::<HANDLE>() as u32,
                None,
                0,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }

    /// This method allows setting a Win32 event that will be signaled by the filter driver when a WAN connection
    /// (such as dial-up, DSL, ADSL, etc.) is established or terminated.
    ///
    /// # Arguments
    ///
    /// * `event_handle`: A `HANDLE` to the Win32 event created by the user application.
    ///
    /// # Returns
    ///
    /// * `Result<()>` - A Result indicating whether setting the WAN event was successful or not. On success,
    ///   returns `Ok(())`. On failure, returns an error.
    pub fn set_wan_event(&self, event_handle: HANDLE) -> Result<()> {
        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_SET_WAN_EVENT,
                Some(&event_handle as *const HANDLE as *const std::ffi::c_void),
                size_of::<HANDLE>() as u32,
                None,
                0,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(())
        }
    }

    /// Retrieves the effective size of the Windows Packet Filter internal intermediate buffer pool.
    ///
    /// # Returns
    ///
    /// * `Result<u32>` - If the operation is successful, returns `Ok(pool_size)` where `pool_size`
    ///   is the size of the intermediate buffer pool. Otherwise, returns an `Err` with the error code.
    ///
    /// This function retrieves the size of the intermediate buffer pool used by the driver.
    /// It uses `DeviceIoControl` with the `IOCTL_NDISRD_QUERY_IB_POOL_SIZE` code to perform the operation.
    pub fn get_intermediate_buffer_pool_size(&self) -> Result<u32> {
        let mut pool_size: u32 = 0;

        let result = unsafe {
            DeviceIoControl(
                self.driver_handle,
                IOCTL_NDISRD_QUERY_IB_POOL_SIZE,
                None,
                0,
                Some(&mut pool_size as *mut u32 as _),
                size_of::<u32>() as u32,
                None,
                None,
            )
        };

        if !result.as_bool() {
            Err(unsafe { GetLastError() }.into())
        } else {
            Ok(pool_size)
        }
    }
}