-
Notifications
You must be signed in to change notification settings - Fork 147
/
winevt.rs
162 lines (135 loc) · 4.77 KB
/
winevt.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
// On Windows pcap libraries support returning an internal event semaphore handle that can be used wake a blocking call that is waiting for an incoming packet.
// This example illustrates how to use this mechanism.
//
// Run with the a capture device as first parameter:
// > cargo run --example winevt -- "\Device\NPF_{D1DCC24C-C89C-45CF-8E62-0D9268331469}"
#[cfg(windows)]
mod windowsonly {
use bitflags::bitflags;
use std::{
sync::{
atomic::{AtomicU32, Ordering},
Arc,
},
thread::{sleep, spawn, JoinHandle},
time::Duration,
};
use windows_sys::Win32::{Foundation::HANDLE, System::Threading::SetEvent};
bitflags! {
pub(crate) struct CmdFlags: u32 {
const KILL = 0b00000001;
const SHOW_STATS = 0b00000010;
const ALL = {
Self::KILL.bits() | Self::SHOW_STATS.bits()
};
}
}
/// Control structure used to signal requests to capture thread.
struct Controller {
jh: Option<JoinHandle<()>>,
cmdreq: Arc<AtomicU32>,
/// Windows Event handle used to wake up the to pcap
hev_wakeup: HANDLE,
}
impl Controller {
/// Tell capture loop thread to terminate.
///
/// Returns once the capture thread is joined.
fn kill(mut self) {
// Set kill command bit
self.cmdreq
.fetch_or(CmdFlags::KILL.bits(), Ordering::SeqCst);
unsafe {
// Wake up the (potentially) blocking catpure call
if SetEvent(self.hev_wakeup) == 0 {
panic!("Unable to signal event");
}
}
if let Some(jh) = self.jh.take() {
let _ = jh.join();
}
}
fn show_stats(&self) {
// Set "show stats" command bit
self.cmdreq
.fetch_or(CmdFlags::SHOW_STATS.bits(), Ordering::SeqCst);
unsafe {
// Wake up the (potentially) blocking catpure call
if SetEvent(self.hev_wakeup) == 0 {
panic!("Unable to signal event");
}
}
}
}
/// Run capture loop. Returns a [`Controller]` that can be used to signal command requests to
/// the capture loop.
fn run_cap_loop(devname: &str) -> Controller {
let cap = pcap::Capture::from_device(devname).unwrap();
let mut cap = cap.open().unwrap();
// Ask pcap for a handle to its internal "wake-up" event semaphore
let hev_wakeup = unsafe { cap.get_event() };
let cmdreq = AtomicU32::new(0);
let cmdreq = Arc::new(cmdreq);
let mut ctrl = Controller {
jh: None,
cmdreq: cmdreq.clone(),
hev_wakeup,
};
let jh = spawn(move || {
let mut pkt_count: u64 = 0;
let mut pkt_size: u64 = 0;
loop {
let res = cap.next_packet();
let cmd = cmdreq.fetch_and(0, Ordering::SeqCst);
if cmd & CmdFlags::KILL.bits() != 0 {
// Controller requested termination
break;
}
if cmd & CmdFlags::SHOW_STATS.bits() != 0 {
// Controller requested that we show some stats
println!("packet count: {pkt_count}");
println!("total packet size: {pkt_size}");
}
match res {
Ok(pkt) => {
println!("Got a packet!");
pkt_count += 1;
pkt_size += pkt.len() as u64;
}
Err(pcap::Error::TimeoutExpired) => {
continue;
}
Err(e) => {
eprintln!("{e}");
break;
}
}
}
});
ctrl.jh = Some(jh);
ctrl
}
pub fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let ctrl = run_cap_loop(&args[0]);
println!("Waiting 1 second ..");
sleep(Duration::from_secs(1));
println!("Tell capture thread to show stats ..");
ctrl.show_stats();
println!("Waiting 1 second ..");
sleep(Duration::from_secs(1));
println!("Tell capture thread to show stats ..");
ctrl.show_stats();
println!("Waiting 1 second ..");
sleep(Duration::from_secs(1));
println!("Tell capture thread to terminate ..");
ctrl.kill();
println!("Done -- bye");
}
}
fn main() {
#[cfg(windows)]
windowsonly::main();
#[cfg(not(windows))]
println!("winevt example is for Windows platforms only");
}