1use std::path::PathBuf;
2
3use clap::Parser;
4use pcap::{Capture, ConnectionStatus, Device, IfFlags};
5
6#[derive(Parser)]
9struct Cli {
10 #[arg(
11 long,
12 help = "name of the interface, use `auto` to determine automatically"
13 )]
14 interface: Option<String>,
15
16 #[arg(
17 long,
18 help = "if `interface` is set, the packets will be written to the `pcap` file, \
19 otherwise the file will be a source of packets"
20 )]
21 path: PathBuf,
22
23 #[arg(long, help = "bpf filter, example: \"udp and not port 443\"")]
24 filter: Option<String>,
25
26 #[arg(long, short)]
28 rng_seed: String,
29}
30
31fn init_logger_std() -> Box<dyn log::Log> {
32 use env_logger::{Builder, Env};
33
34 let env = Env::new().filter_or("RUST_LOG", "debug");
35 let logger = Builder::default().parse_env(env).build();
36 Box::new(logger) as Box<dyn log::Log>
37}
38
39fn main() {
40 log::set_boxed_logger(init_logger_std()).unwrap_or_default();
41 log::set_max_level(log::LevelFilter::max());
42
43 let Cli {
44 interface,
45 path,
46 filter,
47 rng_seed,
48 } = Cli::parse();
49
50 let rng_seed = <[u8; 32]>::try_from(hex::decode(rng_seed).unwrap().as_slice()).unwrap();
51
52 if let Some(name) = interface {
53 sudo::escalate_if_needed().unwrap();
54
55 log::info!("try to choose device");
56 let mut selected = None;
57 match Device::list() {
58 Ok(list) => {
59 for device in list {
60 if name != "auto" {
61 if device.name.eq(&name) {
62 selected = Some(device);
63 break;
64 }
65 } else {
66 log::debug!("candidate: {device:?}");
67 if !device.addresses.is_empty()
68 && device.flags.contains(IfFlags::UP | IfFlags::RUNNING)
69 && matches!(device.flags.connection_status, ConnectionStatus::Connected)
70 {
71 selected = Some(device);
72 }
73 }
74 }
75 }
76 Err(err) => log::error!("{err}"),
77 }
78
79 if let Some(device) = selected {
80 log::info!("will use: {device:?}");
81 let res = Ok(()).and_then(|()| {
82 let mut capture = Capture::from_device(device)?.immediate_mode(true).open()?;
83 capture
84 .filter(&filter.unwrap_or_default(), true)
85 .expect("Failed to apply filter");
86 let savefile = capture.savefile(&path)?;
87
88 webrtc_sniffer::run(capture, Some(savefile), rng_seed)
89 });
90 if let Err(err) = res {
91 log::error!("{err}");
92 }
93 } else {
94 log::error!("cannot find a device: {name}");
95 }
96 } else {
97 log::info!("use file");
98 let res = Ok(()).and_then(|()| {
99 let mut capture = Capture::from_file(&path)?;
100 capture
101 .filter(&filter.unwrap_or_default(), true)
102 .expect("Failed to apply filter");
103 webrtc_sniffer::run(capture, None, rng_seed)
104 });
105 if let Err(err) = res {
106 log::error!("{err}");
107 }
108 }
109}