use crate::Transaction;
use std::io;
use std::os::windows::io::AsRawHandle;
use std::ptr;
use windows_sys::Win32::Foundation::{ERROR_NO_MORE_ITEMS, ERROR_SUCCESS, HANDLE};
use windows_sys::Win32::NetworkManagement::WindowsFilteringPlatform::{
FWPM_FILTER0, FwpmFilterCreateEnumHandle0, FwpmFilterDestroyEnumHandle0, FwpmFilterEnum0,
FwpmFreeMemory0,
};
pub struct FilterEnumerator<'a, 'b: 'a> {
transaction: &'a Transaction<'b>,
enum_handle: HANDLE,
exhausted: bool,
current_entries: *mut *mut FWPM_FILTER0,
current_num_entries: u32,
current_index: u32,
}
impl<'a, 'b> FilterEnumerator<'a, 'b> {
pub fn new(transaction: &'a Transaction<'b>) -> io::Result<Self> {
let mut enum_handle = HANDLE::default();
let status = unsafe {
FwpmFilterCreateEnumHandle0(
transaction.engine.as_raw_handle(),
ptr::null_mut(),
&mut enum_handle,
)
};
if status != ERROR_SUCCESS {
return Err(io::Error::from_raw_os_error(status as i32));
}
Ok(Self {
transaction,
enum_handle,
exhausted: false,
current_entries: ptr::null_mut(),
current_num_entries: 0,
current_index: 0,
})
}
}
impl<'a, 'b> FilterEnumerator<'a, 'b> {
#[allow(clippy::should_implement_trait)]
pub fn next(&mut self) -> Option<io::Result<FilterEnumItem<'a, 'b, '_>>> {
const NUM_ENTRIES: u32 = 50;
if self.exhausted {
return None;
}
if self.current_index < self.current_num_entries {
let idx = usize::try_from(self.current_index).unwrap();
let filter = unsafe { &**self.current_entries.add(idx) };
self.current_index += 1;
return Some(Ok(FilterEnumItem {
filter,
_enumerator: self,
}));
}
let prev_num_entries = self.current_num_entries;
self.free_current_entries();
if prev_num_entries != 0 && prev_num_entries < NUM_ENTRIES {
self.exhausted = true;
return None;
}
let status = unsafe {
FwpmFilterEnum0(
self.transaction.engine.as_raw_handle(),
self.enum_handle,
NUM_ENTRIES,
&mut self.current_entries,
&mut self.current_num_entries,
)
};
self.current_index = 0;
match status {
ERROR_SUCCESS => {
if self.current_num_entries == 0 {
self.exhausted = true;
return None;
}
let filter = unsafe { &**self.current_entries };
self.current_index = 1;
Some(Ok(FilterEnumItem {
filter,
_enumerator: self,
}))
}
ERROR_NO_MORE_ITEMS => {
self.exhausted = true;
None
}
_ => {
self.exhausted = true;
Some(Err(io::Error::from_raw_os_error(status as i32)))
}
}
}
fn free_current_entries(&mut self) {
if !self.current_entries.is_null() {
unsafe { FwpmFreeMemory0((&mut self.current_entries) as *mut _ as *mut _) };
self.current_entries = ptr::null_mut();
self.current_num_entries = 0;
self.current_index = 0;
}
}
}
impl<'a, 'b> Drop for FilterEnumerator<'a, 'b> {
fn drop(&mut self) {
self.free_current_entries();
unsafe {
FwpmFilterDestroyEnumHandle0(self.transaction.engine.as_raw_handle(), self.enum_handle);
}
}
}
pub struct FilterEnumItem<'a, 'b, 'c> {
filter: &'c FWPM_FILTER0,
_enumerator: &'c FilterEnumerator<'a, 'b>,
}
impl<'a, 'b, 'c> FilterEnumItem<'a, 'b, 'c> {
pub fn id(&self) -> u64 {
self.filter.filterId
}
pub fn guid(&self) -> windows_sys::core::GUID {
self.filter.filterKey
}
pub fn provider(&self) -> Option<windows_sys::core::GUID> {
if self.filter.providerKey.is_null() {
None
} else {
Some(unsafe { *self.filter.providerKey })
}
}
pub fn name(&self) -> io::Result<Option<String>> {
if !self.filter.displayData.name.is_null() {
let len = unsafe { wcslen(self.filter.displayData.name) };
let slice = unsafe { std::slice::from_raw_parts(self.filter.displayData.name, len) };
String::from_utf16(slice)
.map_err(|_err| io::Error::other("invalid filter name"))
.map(Some)
} else {
Ok(None)
}
}
pub fn description(&self) -> io::Result<Option<String>> {
if !self.filter.displayData.description.is_null() {
let len = unsafe { wcslen(self.filter.displayData.description) };
let slice =
unsafe { std::slice::from_raw_parts(self.filter.displayData.description, len) };
String::from_utf16(slice)
.map_err(|_err| io::Error::other("invalid filter description"))
.map(Some)
} else {
Ok(None)
}
}
}
unsafe fn wcslen(s: *const u16) -> usize {
let mut current = s;
while unsafe { std::ptr::read_unaligned(current) } != 0 {
current = unsafe { current.add(1) };
}
usize::try_from(unsafe { current.offset_from(s) }).unwrap()
}