use ping_fox::{PingFoxConfig, PingReceive, PingReceiveData, SocketType}; use std::net::Ipv4Addr; use std::sync::{Arc, Condvar, Mutex}; use std::time::Duration; type GenericError = Box; #[derive(Debug)] struct Error { pub message: String, } impl std::fmt::Display for Error { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> { write!(f, "Error")?; if !self.message.is_empty() { write!(f, ": {}", self.message)?; } Ok(()) } } impl std::error::Error for Error { fn source(&self) -> Option<&(dyn std::error::Error + 'static)> { None } } #[derive(Clone)] struct StopCondition { condition: Arc<(Mutex, Condvar)>, } impl StopCondition { pub(crate) fn new() -> Self { Self { condition: Arc::new((Mutex::new(false), Condvar::new())) } } pub(crate) fn set_should_stop(&self) { let (lock, cvar) = &*self.condition; let mut should_stop = lock.lock().unwrap(); *should_stop = true; cvar.notify_all(); } #[allow(dead_code)] pub(crate) fn get_should_stop(&self) -> bool { let (lock, _) = &*self.condition; let should_stop = lock.lock().unwrap(); *should_stop } pub(crate) fn wait_timeout(&self, timeout: Duration) -> bool { let (lock, cvar) = &*self.condition; let guard = lock.lock().unwrap(); let (should_stop, _) = cvar.wait_timeout(guard, timeout).unwrap(); *should_stop } } #[derive(argh::FromArgs)] /// ping - send ICMP ECHO_REQUEST to IP addresses struct Args { #[argh(option, short = 'c', default = "std::u16::MAX")] /// stop after sent ping messages count: u16, #[argh(positional)] /// IP address first_address: String, #[argh(positional)] /// IP addresses more_addresses: Vec, } fn main() -> Result<(), GenericError> { let subscriber = tracing_subscriber::FmtSubscriber::builder() .with_max_level(tracing::Level::WARN) .finish(); tracing::subscriber::set_global_default(subscriber).expect("setting default subscriber failed"); let args: Args = argh::from_env(); let mut addresses: Vec = vec![args.first_address.parse::()?]; for address in args.more_addresses { addresses.push(address.parse::()?); } let config = PingFoxConfig { timeout: Duration::from_secs(1), channel_size: 8, socket_type: SocketType::DGRAM }; let (mut ping_sender, mut ping_receiver) = ping_fox::create(&config)?; let (tx, rx) = std::sync::mpsc::sync_channel(8); let stop_condition_1 = StopCondition::new(); let stop_condition_2 = stop_condition_1.clone(); let thrd2 = std::thread::spawn(move || 'outer: loop { for address in &addresses { match ping_sender.send_to(*address) { Ok(token) => { if let Err(e) = tx.send(token) { println!("ERROR: {:?}", e); } } Err(e) => { println!("ERROR: {:?}", e); break 'outer; } } } let should_stop: bool = stop_condition_1.wait_timeout(Duration::from_secs(1)); if should_stop { break 'outer; } }); let mut i = 0; 'outer: loop { let token = match rx.recv() { Ok(token) => token, Err(e) => { println!("ERROR: {:?}", e); break 'outer; } }; let ping_output = ping_receiver.receive(token); match ping_output { Ok(PingReceive::Data(PingReceiveData { package_size, ip_addr, ttl, sequence_number, ping_duration })) => { println!("{package_size} bytes from {ip_addr}: icmp_seq={sequence_number} ttl={ttl} time={ping_duration:?}",); i += 1; } Ok(PingReceive::Timeout) => { println!("receive timed out"); } Err(e) => { println!("ERROR: {:?}", e); } } if i >= args.count { break 'outer; } } stop_condition_2.set_should_stop(); let join_result = thrd2.join(); if let Err(e) = join_result { println!("ERROR: {:?}", e); } Ok(()) }