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 scopeImplementations§
Source§impl WfpEngine
impl WfpEngine
Sourcepub fn new() -> WfpResult<Self>
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, ¬epad_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
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}Sourcepub fn new_with_flags(flags: u32) -> WfpResult<Self>
pub fn new_with_flags(flags: u32) -> WfpResult<Self>
Sourcepub fn handle(&self) -> HANDLE
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}Trait Implementations§
Auto Trait Implementations§
impl Freeze for WfpEngine
impl RefUnwindSafe for WfpEngine
impl !Sync for WfpEngine
impl Unpin for WfpEngine
impl UnsafeUnpin for WfpEngine
impl UnwindSafe for WfpEngine
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Mutably borrows from an owned value. Read more