1use std::{
2 net::{IpAddr, SocketAddr},
3 time::{Duration, Instant},
4};
5
6use tokio::time::timeout;
7
8use crate::{
9 client::{AsyncSocket, ReplyMap},
10 error::{Result, SurgeError},
11 icmp::{icmpv4, icmpv6, IcmpPacket, PingIdentifier, PingSequence},
12 is_linux_icmp_socket,
13};
14
15pub struct Pinger {
17 pub host: IpAddr,
18 pub ident: Option<PingIdentifier>,
19 timeout: Duration,
20 socket: AsyncSocket,
21 reply_map: ReplyMap,
22 last_sequence: Option<PingSequence>,
23}
24
25impl Drop for Pinger {
26 fn drop(&mut self) {
27 if let Some(sequence) = self.last_sequence.take() {
28 self.reply_map.remove(self.host, self.ident, sequence);
31 }
32 }
33}
34
35impl Pinger {
36 pub(crate) fn new(
37 host: IpAddr,
38 ident_hint: PingIdentifier,
39 socket: AsyncSocket,
40 response_map: ReplyMap,
41 ) -> Pinger {
42 let ident = if is_linux_icmp_socket!(socket.get_type()) {
43 None
44 } else {
45 Some(ident_hint)
46 };
47
48 Pinger {
49 host,
50 ident,
51 timeout: Duration::from_secs(2),
52 socket,
53 reply_map: response_map,
54 last_sequence: None,
55 }
56 }
57
58 pub fn timeout(&mut self, timeout: Duration) -> &mut Pinger {
60 self.timeout = timeout;
61 self
62 }
63
64 pub async fn ping(
66 &mut self,
67 seq: PingSequence,
68 payload: &[u8],
69 ) -> Result<(IcmpPacket, Duration)> {
70 let reply_waiter = self.reply_map.new_waiter(self.host, self.ident, seq)?;
72
73 if let Err(e) = self.send_ping(seq, payload).await {
75 self.reply_map.remove(self.host, self.ident, seq);
76 return Err(e);
77 }
78
79 let send_time = Instant::now();
80 self.last_sequence = Some(seq);
81
82 match timeout(self.timeout, reply_waiter).await {
84 Ok(Ok(reply)) => Ok((
85 reply.packet,
86 reply.timestamp.saturating_duration_since(send_time),
87 )),
88 Ok(Err(_err)) => Err(SurgeError::NetworkError),
89 Err(_) => {
90 self.reply_map.remove(self.host, self.ident, seq);
91 Err(SurgeError::Timeout { seq })
92 }
93 }
94 }
95
96 pub async fn send_ping(&self, seq: PingSequence, payload: &[u8]) -> Result<()> {
98 let mut packet = match self.host {
100 IpAddr::V4(_) => icmpv4::make_icmpv4_echo_packet(
101 self.ident.unwrap_or(PingIdentifier(0)),
102 seq,
103 self.socket.get_type(),
104 payload,
105 )?,
106 IpAddr::V6(_) => icmpv6::make_icmpv6_echo_packet(
107 self.ident.unwrap_or(PingIdentifier(0)),
108 seq,
109 payload,
110 )?,
111 };
112
113 self.socket
114 .send_to(&mut packet, &SocketAddr::new(self.host, 0))
115 .await?;
116
117 Ok(())
118 }
119}