1use crate::engine::WfpEngine;
21use crate::errors::{WfpError, WfpResult};
22use std::path::PathBuf;
23use std::ptr;
24use windows::core::GUID;
25use windows::Win32::Foundation::{ERROR_SUCCESS, HANDLE};
26use windows::Win32::NetworkManagement::WindowsFilteringPlatform::{
27 FwpmFilterCreateEnumHandle0, FwpmFilterDestroyEnumHandle0, FwpmFilterEnum0, FwpmFreeMemory0,
28 FWPM_FILTER0, FWP_ACTION_BLOCK, FWP_ACTION_CALLOUT_INSPECTION, FWP_ACTION_CALLOUT_TERMINATING,
29 FWP_ACTION_CALLOUT_UNKNOWN, FWP_ACTION_PERMIT, FWP_BYTE_BLOB_TYPE, FWP_EMPTY, FWP_UINT16,
30 FWP_UINT32, FWP_UINT64, FWP_UINT8,
31};
32
33use crate::constants::CONDITION_ALE_APP_ID;
34
35#[derive(Debug, Clone, Copy, PartialEq, Eq)]
37pub enum FilterAction {
38 Block,
40 Permit,
42 CalloutTerminating,
44 CalloutInspection,
46 CalloutUnknown,
48 Other(u32),
50}
51
52#[derive(Debug, Clone)]
54pub struct FilterInfo {
55 pub id: u64,
57 pub name: String,
59 pub description: String,
61 pub action: FilterAction,
63 pub provider_key: Option<GUID>,
65 pub weight: u64,
67 pub layer_key: GUID,
69 pub sublayer_key: GUID,
71 pub app_path: Option<PathBuf>,
73 pub num_conditions: u32,
75}
76
77pub struct FilterEnumerator;
99
100impl FilterEnumerator {
101 pub fn all(engine: &WfpEngine) -> WfpResult<Vec<FilterInfo>> {
110 let mut enum_handle = HANDLE::default();
111
112 unsafe {
113 let result = FwpmFilterCreateEnumHandle0(engine.handle(), None, &mut enum_handle);
114
115 if result != ERROR_SUCCESS.0 {
116 return Err(WfpError::Other(format!(
117 "Failed to create filter enum handle: error code {}",
118 result
119 )));
120 }
121 }
122
123 let mut filters = Vec::new();
124 let batch_size = 100;
125
126 loop {
127 let mut filter_array: *mut *mut FWPM_FILTER0 = ptr::null_mut();
128 let mut num_returned: u32 = 0;
129
130 unsafe {
131 let result = FwpmFilterEnum0(
132 engine.handle(),
133 enum_handle,
134 batch_size,
135 &mut filter_array,
136 &mut num_returned,
137 );
138
139 if result != ERROR_SUCCESS.0 {
140 let _ = FwpmFilterDestroyEnumHandle0(engine.handle(), enum_handle);
141 return Err(WfpError::Other(format!(
142 "Failed to enumerate filters: error code {}",
143 result
144 )));
145 }
146
147 if num_returned == 0 {
148 if !filter_array.is_null() {
149 FwpmFreeMemory0(&mut filter_array as *mut _ as *mut *mut _);
150 }
151 break;
152 }
153
154 for i in 0..num_returned {
155 let filter = &**filter_array.offset(i as isize);
156 filters.push(parse_filter(filter));
157 }
158
159 if !filter_array.is_null() {
160 FwpmFreeMemory0(&mut filter_array as *mut _ as *mut *mut _);
161 }
162 }
163 }
164
165 unsafe {
166 let _ = FwpmFilterDestroyEnumHandle0(engine.handle(), enum_handle);
167 }
168
169 Ok(filters)
170 }
171
172 pub fn count(engine: &WfpEngine) -> WfpResult<usize> {
177 let mut enum_handle = HANDLE::default();
178
179 unsafe {
180 let result = FwpmFilterCreateEnumHandle0(engine.handle(), None, &mut enum_handle);
181
182 if result != ERROR_SUCCESS.0 {
183 return Err(WfpError::Other(format!(
184 "Failed to create filter enum handle: error code {}",
185 result
186 )));
187 }
188 }
189
190 let mut count: usize = 0;
191 let batch_size = 100;
192
193 loop {
194 let mut filter_array: *mut *mut FWPM_FILTER0 = ptr::null_mut();
195 let mut num_returned: u32 = 0;
196
197 unsafe {
198 let result = FwpmFilterEnum0(
199 engine.handle(),
200 enum_handle,
201 batch_size,
202 &mut filter_array,
203 &mut num_returned,
204 );
205
206 if result != ERROR_SUCCESS.0 {
207 let _ = FwpmFilterDestroyEnumHandle0(engine.handle(), enum_handle);
208 return Err(WfpError::Other(format!(
209 "Failed to enumerate filters: error code {}",
210 result
211 )));
212 }
213
214 if num_returned == 0 {
215 if !filter_array.is_null() {
216 FwpmFreeMemory0(&mut filter_array as *mut _ as *mut *mut _);
217 }
218 break;
219 }
220
221 count += num_returned as usize;
222
223 if !filter_array.is_null() {
224 FwpmFreeMemory0(&mut filter_array as *mut _ as *mut *mut _);
225 }
226 }
227 }
228
229 unsafe {
230 let _ = FwpmFilterDestroyEnumHandle0(engine.handle(), enum_handle);
231 }
232
233 Ok(count)
234 }
235}
236
237unsafe fn parse_filter(filter: &FWPM_FILTER0) -> FilterInfo {
243 let name = if !filter.displayData.name.is_null() {
244 filter
245 .displayData
246 .name
247 .to_string()
248 .unwrap_or_else(|_| String::new())
249 } else {
250 String::new()
251 };
252
253 let description = if !filter.displayData.description.is_null() {
254 filter
255 .displayData
256 .description
257 .to_string()
258 .unwrap_or_else(|_| String::new())
259 } else {
260 String::new()
261 };
262
263 let action = match filter.action.r#type {
264 FWP_ACTION_BLOCK => FilterAction::Block,
265 FWP_ACTION_PERMIT => FilterAction::Permit,
266 FWP_ACTION_CALLOUT_TERMINATING => FilterAction::CalloutTerminating,
267 FWP_ACTION_CALLOUT_INSPECTION => FilterAction::CalloutInspection,
268 FWP_ACTION_CALLOUT_UNKNOWN => FilterAction::CalloutUnknown,
269 other => FilterAction::Other(other.0),
270 };
271
272 let provider_key = if !filter.providerKey.is_null() {
273 Some(*filter.providerKey)
274 } else {
275 None
276 };
277
278 let weight = match filter.weight.r#type {
279 FWP_UINT8 => filter.weight.Anonymous.uint8 as u64,
280 FWP_UINT16 => filter.weight.Anonymous.uint16 as u64,
281 FWP_UINT32 => filter.weight.Anonymous.uint32 as u64,
282 FWP_UINT64 => {
283 let ptr = filter.weight.Anonymous.uint64;
284 if ptr.is_null() {
285 0
286 } else {
287 *ptr
288 }
289 }
290 FWP_EMPTY => 0,
291 _ => 0,
292 };
293
294 let app_path = extract_app_path(filter);
295
296 FilterInfo {
297 id: filter.filterId,
298 name,
299 description,
300 action,
301 provider_key,
302 weight,
303 layer_key: filter.layerKey,
304 sublayer_key: filter.subLayerKey,
305 app_path,
306 num_conditions: filter.numFilterConditions,
307 }
308}
309
310unsafe fn extract_app_path(filter: &FWPM_FILTER0) -> Option<PathBuf> {
315 if filter.numFilterConditions == 0 || filter.filterCondition.is_null() {
316 return None;
317 }
318
319 let conditions =
320 std::slice::from_raw_parts(filter.filterCondition, filter.numFilterConditions as usize);
321
322 let condition = conditions
323 .iter()
324 .find(|c| c.fieldKey == CONDITION_ALE_APP_ID)?;
325
326 if condition.conditionValue.r#type != FWP_BYTE_BLOB_TYPE {
327 return None;
328 }
329
330 let blob_ptr = condition.conditionValue.Anonymous.byteBlob;
331 if blob_ptr.is_null() {
332 return None;
333 }
334
335 let blob = &*blob_ptr;
336 if blob.data.is_null() || blob.size == 0 || (blob.size % 2) != 0 {
337 return None;
338 }
339
340 let wide_slice = std::slice::from_raw_parts(blob.data as *const u16, (blob.size / 2) as usize);
341 let null_pos = wide_slice
342 .iter()
343 .position(|&c| c == 0)
344 .unwrap_or(wide_slice.len());
345
346 String::from_utf16(&wide_slice[..null_pos])
347 .ok()
348 .map(PathBuf::from)
349}
350
351#[cfg(test)]
352mod tests {
353 use super::*;
354
355 #[test]
356 fn test_filter_action_eq() {
357 assert_eq!(FilterAction::Block, FilterAction::Block);
358 assert_eq!(FilterAction::Permit, FilterAction::Permit);
359 assert_ne!(FilterAction::Block, FilterAction::Permit);
360 assert_eq!(FilterAction::Other(42), FilterAction::Other(42));
361 assert_ne!(FilterAction::Other(1), FilterAction::Other(2));
362 }
363
364 #[test]
365 fn test_filter_action_copy() {
366 let action = FilterAction::Block;
367 let copy = action;
368 assert_eq!(action, copy);
369 }
370
371 #[test]
372 fn test_filter_info_clone() {
373 let info = FilterInfo {
374 id: 42,
375 name: "Test filter".to_string(),
376 description: "A test".to_string(),
377 action: FilterAction::Block,
378 provider_key: None,
379 weight: 1000,
380 layer_key: GUID::zeroed(),
381 sublayer_key: GUID::zeroed(),
382 app_path: Some(PathBuf::from(r"C:\test.exe")),
383 num_conditions: 1,
384 };
385
386 let cloned = info.clone();
387 assert_eq!(cloned.id, 42);
388 assert_eq!(cloned.name, "Test filter");
389 assert_eq!(cloned.action, FilterAction::Block);
390 assert!(cloned.app_path.is_some());
391 }
392
393 #[test]
394 fn test_filter_info_default_values() {
395 let info = FilterInfo {
396 id: 0,
397 name: String::new(),
398 description: String::new(),
399 action: FilterAction::Permit,
400 provider_key: None,
401 weight: 0,
402 layer_key: GUID::zeroed(),
403 sublayer_key: GUID::zeroed(),
404 app_path: None,
405 num_conditions: 0,
406 };
407
408 assert_eq!(info.id, 0);
409 assert!(info.name.is_empty());
410 assert!(info.provider_key.is_none());
411 assert!(info.app_path.is_none());
412 }
413
414 #[test]
415 fn test_filter_info_with_provider() {
416 let guid = GUID::from_u128(0x12345678_1234_5678_1234_567812345678);
417 let info = FilterInfo {
418 id: 100,
419 name: "Provider filter".to_string(),
420 description: String::new(),
421 action: FilterAction::Block,
422 provider_key: Some(guid),
423 weight: 5000,
424 layer_key: GUID::zeroed(),
425 sublayer_key: GUID::zeroed(),
426 app_path: None,
427 num_conditions: 3,
428 };
429
430 assert_eq!(info.provider_key, Some(guid));
431 assert_eq!(info.num_conditions, 3);
432 }
433
434 #[test]
435 #[ignore] fn test_enumerate_all_filters() {
437 let engine = WfpEngine::new().expect("Failed to open WFP engine");
438 let filters = FilterEnumerator::all(&engine).expect("Failed to enumerate");
439 assert!(!filters.is_empty(), "Expected at least one WFP filter");
441 }
442
443 #[test]
444 #[ignore] fn test_count_filters() {
446 let engine = WfpEngine::new().expect("Failed to open WFP engine");
447 let count = FilterEnumerator::count(&engine).expect("Failed to count");
448 let all = FilterEnumerator::all(&engine).expect("Failed to enumerate");
449 assert_eq!(count, all.len());
450 }
451}