Skip to main content

WfpEngine

Struct WfpEngine 

Source
pub struct WfpEngine { /* private fields */ }
Expand description

WFP Engine session with RAII handle management

Opens a session to the Windows Filtering Platform on creation and automatically closes it on drop.

§Examples

use windows_wfp::WfpEngine;

let engine = WfpEngine::new()?;
// Use engine for filter operations
// Session automatically closed when engine goes out of scope

Implementations§

Source§

impl WfpEngine

Source

pub fn new() -> WfpResult<Self>

Open a new WFP engine session

Requires administrator privileges.

§Errors

Returns WfpError::EngineOpenFailed if:

  • Insufficient permissions (not running as admin)
  • WFP service not available
  • Session creation failed
§Examples
use windows_wfp::WfpEngine;

let engine = WfpEngine::new()?;
Examples found in repository?
examples/simple_block.rs (line 29)
24fn main() -> WfpResult<()> {
25    println!("TRusTY Wall - Simple Block Demo\n");
26
27    // Initialize WFP
28    println!("Opening WFP Engine...");
29    let engine = WfpEngine::new()?;
30    println!("Engine opened\n");
31
32    println!("Registering provider...");
33    initialize_wfp(&engine)?;
34    println!("Provider registered\n");
35
36    // Block notepad.exe outbound connections
37    println!("Adding block filter for notepad.exe...");
38    let notepad_rule = FilterRule::new("Block Notepad", Direction::Outbound, Action::Block)
39        .with_weight(FilterWeight::UserBlock)
40        .with_app_path(r"C:\Windows\System32\notepad.exe");
41
42    let filter_id = FilterBuilder::add_filter(&engine, &notepad_rule)?;
43    println!("Filter added (ID: {})\n", filter_id);
44
45    println!("Filter active for 10 seconds...");
46    println!("   (Try opening notepad.exe and accessing network)\n");
47
48    for i in (1..=10).rev() {
49        println!("   {} seconds remaining...", i);
50        thread::sleep(Duration::from_secs(1));
51    }
52
53    println!("\nRemoving filter...");
54    FilterBuilder::delete_filter(&engine, filter_id)?;
55    println!("Filter removed\n");
56
57    println!("Demo complete!");
58    Ok(())
59}
More examples
Hide additional examples
examples/live_demo.rs (line 48)
32fn main() -> WfpResult<()> {
33    println!("TRusTY Wall - Live WFP Demo");
34    println!("================================\n");
35
36    // Check for admin privileges
37    if !is_elevated() {
38        eprintln!("ERROR: This demo requires Administrator privileges!");
39        eprintln!("   Please run: cargo run --example live_demo --release");
40        eprintln!("   from an Administrator command prompt.\n");
41        std::process::exit(1);
42    }
43
44    println!("Running with Administrator privileges\n");
45
46    // Step 1: Initialize WFP Engine
47    println!("Step 1: Opening WFP Engine session...");
48    let engine = WfpEngine::new()?;
49    println!("   Engine session opened\n");
50
51    // Step 2: Register Provider & Sublayer
52    println!("Step 2: Registering WFP provider & sublayer...");
53    initialize_wfp(&engine)?;
54    println!("   Provider & sublayer registered\n");
55
56    // Step 3: Subscribe to network events
57    println!("Step 3: Subscribing to network events...");
58    let event_subscription = WfpEventSubscription::new(&engine)?;
59    println!("   Event subscription active\n");
60
61    // Step 4: Add blocking filter for curl.exe
62    println!("Step 4: Adding block filter for curl.exe...");
63    let curl_path = find_curl_path();
64    println!("   Target: {}", curl_path.display());
65
66    let block_rule = FilterRule::new("Block curl.exe", Direction::Outbound, Action::Block)
67        .with_weight(FilterWeight::UserBlock)
68        .with_app_path(curl_path.clone());
69
70    let filter_id = FilterBuilder::add_filter(&engine, &block_rule)?;
71    println!("   Filter added (ID: {})\n", filter_id);
72
73    // Step 5: Monitor events
74    println!("Step 5: Monitoring network events...");
75    println!("   Press Ctrl+C to stop\n");
76    println!("TIP: In another terminal, run:");
77    println!("   > curl https://google.com");
78    println!("   You should see the connection BLOCKED below!\n");
79    println!("===================================================\n");
80
81    let start_time = std::time::Instant::now();
82    let mut event_count = 0;
83
84    loop {
85        match event_subscription.try_recv() {
86            Ok(event) => {
87                event_count += 1;
88                print_event(&event, event_count);
89            }
90            Err(std::sync::mpsc::TryRecvError::Empty) => {
91                thread::sleep(Duration::from_millis(100));
92            }
93            Err(std::sync::mpsc::TryRecvError::Disconnected) => {
94                println!("\nEvent channel disconnected!");
95                break;
96            }
97        }
98
99        // Auto-stop after 60 seconds for demo
100        if start_time.elapsed() > Duration::from_secs(60) {
101            println!("\nDemo timeout (60s) - stopping...");
102            break;
103        }
104    }
105
106    // Cleanup
107    println!("\nCleaning up...");
108    FilterBuilder::delete_filter(&engine, filter_id)?;
109    println!("   Filter removed");
110    drop(event_subscription);
111    println!("   Event subscription closed");
112    drop(engine);
113    println!("   Engine session closed\n");
114
115    println!("Demo complete! {} events captured.", event_count);
116    Ok(())
117}
examples/list_filters.rs (line 26)
21fn main() -> WfpResult<()> {
22    println!("🔍 TRusTY Wall - WFP Filter Enumeration\n");
23
24    // Open WFP engine
25    println!("📡 Opening WFP Engine...");
26    let engine = WfpEngine::new()?;
27    println!("✓ Engine opened\n");
28
29    // Create enumeration handle
30    let mut enum_handle = HANDLE::default();
31
32    unsafe {
33        // Pass NULL template to enumerate ALL filters
34        let result = FwpmFilterCreateEnumHandle0(
35            engine.handle(),
36            None, // NULL template = enumerate all filters
37            &mut enum_handle,
38        );
39
40        if result != ERROR_SUCCESS.0 {
41            eprintln!(
42                "Failed to create enum handle: error {} (0x{:X})",
43                result, result
44            );
45            return Ok(());
46        }
47    }
48
49    println!("📋 Enumerating WFP filters...\n");
50    println!("{:-<180}", "");
51    println!(
52        "{:<10} {:<40} {:<15} {:<15} {:<15} {:<60}",
53        "Filter ID", "Name", "Action", "Provider", "Weight", "App Path"
54    );
55    println!("{:-<180}", "");
56
57    let mut total_filters = 0;
58    let mut trusty_filters = 0;
59    let mut page = 0;
60
61    loop {
62        let mut filters: *mut *mut FWPM_FILTER0 = ptr::null_mut();
63        let mut num_returned: u32 = 0;
64
65        unsafe {
66            let result = FwpmFilterEnum0(
67                engine.handle(),
68                enum_handle,
69                100, // Request 100 filters at a time
70                &mut filters,
71                &mut num_returned,
72            );
73
74            if result != ERROR_SUCCESS.0 {
75                break;
76            }
77
78            if num_returned == 0 {
79                break;
80            }
81
82            page += 1;
83
84            // Process returned filters
85            for i in 0..num_returned {
86                let filter = &**filters.offset(i as isize);
87
88                // Get filter name
89                let name = if !filter.displayData.name.is_null() {
90                    filter
91                        .displayData
92                        .name
93                        .to_string()
94                        .unwrap_or_else(|_| "Unknown".to_string())
95                } else {
96                    "Unknown".to_string()
97                };
98
99                // Get action - FWP_ACTION_TYPE: 0x1=BLOCK, 0x2=PERMIT, 0x4=CALLOUT
100                let action = match filter.action.r#type {
101                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_ACTION_BLOCK => "BLOCK",
102                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_ACTION_PERMIT => "PERMIT",
103                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_ACTION_CALLOUT_TERMINATING => "CALLOUT_TERM",
104                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_ACTION_CALLOUT_INSPECTION => "CALLOUT_INSP",
105                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_ACTION_CALLOUT_UNKNOWN => "CALLOUT_UNK",
106                    _ => "OTHER",
107                };
108
109                // Get provider name by reading providerKey GUID
110                let provider = if !filter.providerKey.is_null() {
111                    let provider_guid = &*filter.providerKey;
112                    // TinyWall GUID: check if it matches known GUIDs
113                    format!("{:?}", provider_guid)
114                        .chars()
115                        .take(12)
116                        .collect::<String>()
117                } else {
118                    "System".to_string()
119                };
120
121                // Check if it's our filter
122                let is_trusty = name.contains("TRusTY")
123                    || name.contains("Block curl")
124                    || name.contains("Block Notepad");
125                if is_trusty {
126                    trusty_filters += 1;
127                }
128
129                // Get weight - FWP_VALUE0 can have different types
130                let weight = match filter.weight.r#type {
131                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_UINT8 => {
132                        filter.weight.Anonymous.uint8 as u64
133                    }
134                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_UINT16 => {
135                        filter.weight.Anonymous.uint16 as u64
136                    }
137                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_UINT32 => {
138                        filter.weight.Anonymous.uint32 as u64
139                    }
140                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_UINT64 => {
141                        *filter.weight.Anonymous.uint64
142                    }
143                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_EMPTY => 0,
144                    _ => 0,
145                };
146
147                // Format name with truncation (char-boundary safe)
148                let display_name = if name.chars().count() > 40 {
149                    let truncated: String = name.chars().take(37).collect();
150                    format!("{}...", truncated)
151                } else {
152                    name.clone()
153                };
154
155                // Extract APP_ID (application path) from filter conditions
156                let app_path = if filter.numFilterConditions > 0
157                    && !filter.filterCondition.is_null()
158                {
159                    let conditions = std::slice::from_raw_parts(
160                        filter.filterCondition,
161                        filter.numFilterConditions as usize,
162                    );
163
164                    // Look for FWPM_CONDITION_ALE_APP_ID
165                    conditions.iter()
166                        .find(|c| {
167                            let guid = &c.fieldKey;
168                            // FWPM_CONDITION_ALE_APP_ID = {d78e1e87-8644-4ea5-9437-d809ecefc971}
169                            format!("{:?}", guid).to_lowercase().contains("d78e1e87")
170                        })
171                        .and_then(|condition| {
172                            // APP_ID is a FWP_BYTE_BLOB_TYPE
173                            if condition.conditionValue.r#type == windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_BYTE_BLOB_TYPE {
174                                let blob = &*condition.conditionValue.Anonymous.byteBlob;
175                                if !blob.data.is_null() && blob.size > 0 {
176                                    // APP_ID is a wide string
177                                    let wide_slice = std::slice::from_raw_parts(
178                                        blob.data as *const u16,
179                                        (blob.size / 2) as usize
180                                    );
181                                    // Find null terminator
182                                    let null_pos = wide_slice.iter().position(|&c| c == 0).unwrap_or(wide_slice.len());
183                                    String::from_utf16(&wide_slice[..null_pos]).ok()
184                                } else {
185                                    None
186                                }
187                            } else {
188                                None
189                            }
190                        })
191                        .unwrap_or_else(String::new)
192                } else {
193                    String::new()
194                };
195
196                // Truncate app path for display (char-boundary safe)
197                let display_path = if app_path.chars().count() > 60 {
198                    let chars: Vec<char> = app_path.chars().collect();
199                    let start_idx = chars.len().saturating_sub(57);
200                    let truncated: String = chars[start_idx..].iter().collect();
201                    format!("...{}", truncated)
202                } else {
203                    app_path.clone()
204                };
205
206                // Highlight our filters
207                if is_trusty {
208                    println!(
209                        "\x1b[32m{:<10} {:<40} {:<15} {:<15} {:<15} {:<60}\x1b[0m",
210                        filter.filterId, display_name, action, provider, weight, display_path
211                    );
212                } else {
213                    println!(
214                        "{:<10} {:<40} {:<15} {:<15} {:<15} {:<60}",
215                        filter.filterId, display_name, action, provider, weight, display_path
216                    );
217                }
218
219                total_filters += 1;
220            }
221
222            // Free the returned array - FwpmFreeMemory0 expects void**
223            if !filters.is_null() {
224                windows::Win32::NetworkManagement::WindowsFilteringPlatform::FwpmFreeMemory0(
225                    &mut filters as *mut _ as *mut *mut _,
226                );
227            }
228        }
229    }
230
231    println!("{:-<180}", "");
232    println!("\n📊 Summary:");
233    println!("   Total filters: {}", total_filters);
234    println!("   TRusTY Wall filters: {}", trusty_filters);
235    println!("   Pages enumerated: {}", page);
236
237    // Cleanup
238    unsafe {
239        let _ = FwpmFilterDestroyEnumHandle0(engine.handle(), enum_handle);
240    }
241
242    println!("\n✨ Enumeration complete!");
243    Ok(())
244}
Source

pub fn new_with_flags(flags: u32) -> WfpResult<Self>

Open a new WFP engine session with custom flags

§Arguments
  • flags - Session flags (e.g., FWPM_SESSION_FLAG_DYNAMIC for automatic cleanup)
§Errors

Returns WfpError::EngineOpenFailed if session creation fails.

Source

pub fn handle(&self) -> HANDLE

Get raw handle to WFP engine session

§Safety

The handle is only valid while this WfpEngine instance is alive. Do not close the handle manually - it will be closed automatically on drop.

Examples found in repository?
examples/list_filters.rs (line 35)
21fn main() -> WfpResult<()> {
22    println!("🔍 TRusTY Wall - WFP Filter Enumeration\n");
23
24    // Open WFP engine
25    println!("📡 Opening WFP Engine...");
26    let engine = WfpEngine::new()?;
27    println!("✓ Engine opened\n");
28
29    // Create enumeration handle
30    let mut enum_handle = HANDLE::default();
31
32    unsafe {
33        // Pass NULL template to enumerate ALL filters
34        let result = FwpmFilterCreateEnumHandle0(
35            engine.handle(),
36            None, // NULL template = enumerate all filters
37            &mut enum_handle,
38        );
39
40        if result != ERROR_SUCCESS.0 {
41            eprintln!(
42                "Failed to create enum handle: error {} (0x{:X})",
43                result, result
44            );
45            return Ok(());
46        }
47    }
48
49    println!("📋 Enumerating WFP filters...\n");
50    println!("{:-<180}", "");
51    println!(
52        "{:<10} {:<40} {:<15} {:<15} {:<15} {:<60}",
53        "Filter ID", "Name", "Action", "Provider", "Weight", "App Path"
54    );
55    println!("{:-<180}", "");
56
57    let mut total_filters = 0;
58    let mut trusty_filters = 0;
59    let mut page = 0;
60
61    loop {
62        let mut filters: *mut *mut FWPM_FILTER0 = ptr::null_mut();
63        let mut num_returned: u32 = 0;
64
65        unsafe {
66            let result = FwpmFilterEnum0(
67                engine.handle(),
68                enum_handle,
69                100, // Request 100 filters at a time
70                &mut filters,
71                &mut num_returned,
72            );
73
74            if result != ERROR_SUCCESS.0 {
75                break;
76            }
77
78            if num_returned == 0 {
79                break;
80            }
81
82            page += 1;
83
84            // Process returned filters
85            for i in 0..num_returned {
86                let filter = &**filters.offset(i as isize);
87
88                // Get filter name
89                let name = if !filter.displayData.name.is_null() {
90                    filter
91                        .displayData
92                        .name
93                        .to_string()
94                        .unwrap_or_else(|_| "Unknown".to_string())
95                } else {
96                    "Unknown".to_string()
97                };
98
99                // Get action - FWP_ACTION_TYPE: 0x1=BLOCK, 0x2=PERMIT, 0x4=CALLOUT
100                let action = match filter.action.r#type {
101                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_ACTION_BLOCK => "BLOCK",
102                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_ACTION_PERMIT => "PERMIT",
103                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_ACTION_CALLOUT_TERMINATING => "CALLOUT_TERM",
104                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_ACTION_CALLOUT_INSPECTION => "CALLOUT_INSP",
105                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_ACTION_CALLOUT_UNKNOWN => "CALLOUT_UNK",
106                    _ => "OTHER",
107                };
108
109                // Get provider name by reading providerKey GUID
110                let provider = if !filter.providerKey.is_null() {
111                    let provider_guid = &*filter.providerKey;
112                    // TinyWall GUID: check if it matches known GUIDs
113                    format!("{:?}", provider_guid)
114                        .chars()
115                        .take(12)
116                        .collect::<String>()
117                } else {
118                    "System".to_string()
119                };
120
121                // Check if it's our filter
122                let is_trusty = name.contains("TRusTY")
123                    || name.contains("Block curl")
124                    || name.contains("Block Notepad");
125                if is_trusty {
126                    trusty_filters += 1;
127                }
128
129                // Get weight - FWP_VALUE0 can have different types
130                let weight = match filter.weight.r#type {
131                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_UINT8 => {
132                        filter.weight.Anonymous.uint8 as u64
133                    }
134                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_UINT16 => {
135                        filter.weight.Anonymous.uint16 as u64
136                    }
137                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_UINT32 => {
138                        filter.weight.Anonymous.uint32 as u64
139                    }
140                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_UINT64 => {
141                        *filter.weight.Anonymous.uint64
142                    }
143                    windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_EMPTY => 0,
144                    _ => 0,
145                };
146
147                // Format name with truncation (char-boundary safe)
148                let display_name = if name.chars().count() > 40 {
149                    let truncated: String = name.chars().take(37).collect();
150                    format!("{}...", truncated)
151                } else {
152                    name.clone()
153                };
154
155                // Extract APP_ID (application path) from filter conditions
156                let app_path = if filter.numFilterConditions > 0
157                    && !filter.filterCondition.is_null()
158                {
159                    let conditions = std::slice::from_raw_parts(
160                        filter.filterCondition,
161                        filter.numFilterConditions as usize,
162                    );
163
164                    // Look for FWPM_CONDITION_ALE_APP_ID
165                    conditions.iter()
166                        .find(|c| {
167                            let guid = &c.fieldKey;
168                            // FWPM_CONDITION_ALE_APP_ID = {d78e1e87-8644-4ea5-9437-d809ecefc971}
169                            format!("{:?}", guid).to_lowercase().contains("d78e1e87")
170                        })
171                        .and_then(|condition| {
172                            // APP_ID is a FWP_BYTE_BLOB_TYPE
173                            if condition.conditionValue.r#type == windows::Win32::NetworkManagement::WindowsFilteringPlatform::FWP_BYTE_BLOB_TYPE {
174                                let blob = &*condition.conditionValue.Anonymous.byteBlob;
175                                if !blob.data.is_null() && blob.size > 0 {
176                                    // APP_ID is a wide string
177                                    let wide_slice = std::slice::from_raw_parts(
178                                        blob.data as *const u16,
179                                        (blob.size / 2) as usize
180                                    );
181                                    // Find null terminator
182                                    let null_pos = wide_slice.iter().position(|&c| c == 0).unwrap_or(wide_slice.len());
183                                    String::from_utf16(&wide_slice[..null_pos]).ok()
184                                } else {
185                                    None
186                                }
187                            } else {
188                                None
189                            }
190                        })
191                        .unwrap_or_else(String::new)
192                } else {
193                    String::new()
194                };
195
196                // Truncate app path for display (char-boundary safe)
197                let display_path = if app_path.chars().count() > 60 {
198                    let chars: Vec<char> = app_path.chars().collect();
199                    let start_idx = chars.len().saturating_sub(57);
200                    let truncated: String = chars[start_idx..].iter().collect();
201                    format!("...{}", truncated)
202                } else {
203                    app_path.clone()
204                };
205
206                // Highlight our filters
207                if is_trusty {
208                    println!(
209                        "\x1b[32m{:<10} {:<40} {:<15} {:<15} {:<15} {:<60}\x1b[0m",
210                        filter.filterId, display_name, action, provider, weight, display_path
211                    );
212                } else {
213                    println!(
214                        "{:<10} {:<40} {:<15} {:<15} {:<15} {:<60}",
215                        filter.filterId, display_name, action, provider, weight, display_path
216                    );
217                }
218
219                total_filters += 1;
220            }
221
222            // Free the returned array - FwpmFreeMemory0 expects void**
223            if !filters.is_null() {
224                windows::Win32::NetworkManagement::WindowsFilteringPlatform::FwpmFreeMemory0(
225                    &mut filters as *mut _ as *mut *mut _,
226                );
227            }
228        }
229    }
230
231    println!("{:-<180}", "");
232    println!("\n📊 Summary:");
233    println!("   Total filters: {}", total_filters);
234    println!("   TRusTY Wall filters: {}", trusty_filters);
235    println!("   Pages enumerated: {}", page);
236
237    // Cleanup
238    unsafe {
239        let _ = FwpmFilterDestroyEnumHandle0(engine.handle(), enum_handle);
240    }
241
242    println!("\n✨ Enumeration complete!");
243    Ok(())
244}
Source

pub fn is_valid(&self) -> bool

Check if session is valid

Trait Implementations§

Source§

impl Debug for WfpEngine

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl Drop for WfpEngine

Source§

fn drop(&mut self)

Automatically close WFP engine session when dropped

Source§

impl Send for WfpEngine

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.