Skip to main content

live_demo/
live_demo.rs

1//! Live WFP Demo - Block Application & Monitor Events
2//!
3//! This example demonstrates the complete WFP implementation:
4//! 1. Initialize WFP provider/sublayer
5//! 2. Block a specific application (curl.exe)
6//! 3. Subscribe to network events
7//! 4. Monitor blocked connections in real-time
8//!
9//! # Usage
10//!
11//! **REQUIRES ADMINISTRATOR PRIVILEGES**
12//!
13//! ```bash
14//! cargo run --example live_demo --release
15//! ```
16//!
17//! Then in another terminal, try:
18//! ```bash
19//! curl https://google.com
20//! ```
21//!
22//! You should see the connection blocked and logged!
23
24use std::path::PathBuf;
25use std::thread;
26use std::time::Duration;
27use windows_wfp::{
28    initialize_wfp, Action, Direction, FilterBuilder, FilterRule, FilterWeight, NetworkEvent,
29    WfpEngine, WfpEventSubscription, WfpResult,
30};
31
32fn main() -> WfpResult<()> {
33    println!("windows-wfp - 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}
118
119fn print_event(event: &NetworkEvent, count: usize) {
120    println!("Event #{}: {:?}", count, event.event_type);
121    println!("   Timestamp:   {:?}", event.timestamp);
122
123    if let Some(ref path) = event.app_path {
124        println!("   Application: {}", path.display());
125    }
126
127    if let Some(local) = event.local_addr {
128        println!("   Local:       {}:{}", local, event.local_port);
129    }
130
131    if let Some(remote) = event.remote_addr {
132        println!("   Remote:      {}:{}", remote, event.remote_port);
133    }
134
135    println!("   Protocol:    {}", event.protocol);
136
137    if let Some(filter_id) = event.filter_id {
138        println!("   Filter ID:   {}", filter_id);
139    }
140
141    if let Some(layer_id) = event.layer_id {
142        println!("   Layer ID:    {}", layer_id);
143    }
144
145    println!();
146}
147
148fn find_curl_path() -> PathBuf {
149    let candidates = vec![
150        r"C:\Windows\System32\curl.exe",
151        r"C:\Program Files\Git\mingw64\bin\curl.exe",
152        r"C:\tools\curl\bin\curl.exe",
153    ];
154
155    for path in candidates {
156        let p = PathBuf::from(path);
157        if p.exists() {
158            return p;
159        }
160    }
161
162    PathBuf::from(r"C:\Windows\System32\curl.exe")
163}
164
165fn is_elevated() -> bool {
166    #[cfg(windows)]
167    {
168        use std::mem;
169        use windows::Win32::Foundation::{CloseHandle, HANDLE};
170        use windows::Win32::Security::{
171            GetTokenInformation, TokenElevation, TOKEN_ELEVATION, TOKEN_QUERY,
172        };
173        use windows::Win32::System::Threading::{GetCurrentProcess, OpenProcessToken};
174
175        unsafe {
176            let mut token: HANDLE = HANDLE::default();
177            let process = GetCurrentProcess();
178
179            if OpenProcessToken(process, TOKEN_QUERY, &mut token).is_err() {
180                return false;
181            }
182
183            let mut elevation = TOKEN_ELEVATION { TokenIsElevated: 0 };
184            let mut returned_length: u32 = 0;
185
186            let result = GetTokenInformation(
187                token,
188                TokenElevation,
189                Some(&mut elevation as *mut _ as *mut _),
190                mem::size_of::<TOKEN_ELEVATION>() as u32,
191                &mut returned_length,
192            );
193
194            let _ = CloseHandle(token);
195
196            result.is_ok() && elevation.TokenIsElevated != 0
197        }
198    }
199
200    #[cfg(not(windows))]
201    false
202}