use crate::errors::{WfpError, WfpResult};
use std::ptr;
use windows::core::{PCWSTR, PWSTR};
use windows::Win32::Foundation::{ERROR_SUCCESS, HANDLE};
use windows::Win32::NetworkManagement::WindowsFilteringPlatform::{
FwpmEngineClose0, FwpmEngineOpen0, FWPM_SESSION0, FWPM_SESSION_FLAG_DYNAMIC,
};
#[derive(Debug)]
pub struct WfpEngine {
handle: HANDLE,
}
impl WfpEngine {
pub fn new() -> WfpResult<Self> {
Self::new_with_flags(FWPM_SESSION_FLAG_DYNAMIC)
}
pub fn new_with_flags(flags: u32) -> WfpResult<Self> {
let session = FWPM_SESSION0 {
sessionKey: windows::core::GUID::zeroed(),
displayData: Default::default(),
flags,
txnWaitTimeoutInMSec: 0,
processId: 0,
sid: ptr::null_mut(),
username: PWSTR::null(),
kernelMode: false.into(),
};
let mut handle = HANDLE::default();
unsafe {
let result = FwpmEngineOpen0(
PCWSTR::null(),
10, None,
Some(&session),
&mut handle,
);
if result != ERROR_SUCCESS.0 {
return match result {
5 => Err(WfpError::InsufficientPermissions),
1062 | 1075 => Err(WfpError::ServiceNotAvailable),
_ => Err(WfpError::Other(format!(
"FwpmEngineOpen0 failed with Windows error code: {} (0x{:X}). \
Common causes: BFE service not running, insufficient privileges, or WFP driver not loaded.",
result, result
))),
};
}
}
Ok(Self { handle })
}
pub fn handle(&self) -> HANDLE {
self.handle
}
pub fn is_valid(&self) -> bool {
!self.handle.is_invalid()
}
}
impl Drop for WfpEngine {
fn drop(&mut self) {
if !self.handle.is_invalid() {
unsafe {
let _ = FwpmEngineClose0(self.handle);
}
}
}
}
unsafe impl Send for WfpEngine {}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[ignore] fn test_engine_creation() {
let engine = WfpEngine::new();
assert!(engine.is_ok(), "Failed to create WFP engine (run as admin)");
if let Ok(engine) = engine {
assert!(engine.is_valid());
assert!(!engine.handle().is_invalid());
}
}
#[test]
#[ignore] fn test_engine_drop_closes_session() {
{
let _engine = WfpEngine::new().expect("Failed to create engine");
}
}
#[test]
fn test_engine_without_permissions() {
let result = WfpEngine::new();
if let Err(err) = result {
match err {
WfpError::InsufficientPermissions => (),
WfpError::ServiceNotAvailable => (),
WfpError::EngineOpenFailed => (),
other => panic!("Unexpected error: {:?}", other),
}
}
}
}