icmp_client/
lib.rs

1//
2use std::{io::Error as IoError, net::SocketAddr};
3
4use async_trait::async_trait;
5
6#[async_trait]
7pub trait AsyncClient {
8    fn with_config(config: &Config) -> Result<Self, AsyncClientWithConfigError>
9    where
10        Self: Sized;
11
12    async fn send_to<A: Into<SocketAddr> + Send>(
13        &self,
14        buf: &[u8],
15        addr: A,
16    ) -> Result<usize, IoError>;
17    async fn recv_from(&self, buf: &mut [u8]) -> Result<(usize, SocketAddr), IoError>;
18}
19
20//
21#[derive(Debug)]
22pub enum AsyncClientWithConfigError {
23    IcmpV6ProtocolNotSupported(IoError),
24    OtherIoError(IoError),
25}
26impl core::fmt::Display for AsyncClientWithConfigError {
27    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
28        write!(f, "{self:?}")
29    }
30}
31impl std::error::Error for AsyncClientWithConfigError {}
32
33impl From<IoError> for AsyncClientWithConfigError {
34    fn from(err: IoError) -> Self {
35        Self::OtherIoError(err)
36    }
37}
38
39//
40pub mod config;
41pub use config::Config;
42
43pub mod utils;
44
45//
46#[cfg(feature = "impl_async_io")]
47pub mod impl_async_io;
48#[cfg(feature = "impl_tokio")]
49pub mod impl_tokio;
50
51#[cfg(any(feature = "impl_async_io", feature = "impl_tokio"))]
52#[cfg(test)]
53pub(crate) mod tests_helper {
54    use super::*;
55
56    use std::net::{Ipv4Addr, Ipv6Addr};
57
58    use icmp_packet::{Icmpv4, Icmpv6, PayloadLengthDelimitedEchoRequest};
59
60    pub(crate) async fn ping_ipv4<C: AsyncClient>(
61        ip: Ipv4Addr,
62    ) -> Result<(), Box<dyn std::error::Error>> {
63        // ipv4
64        let client = C::with_config(&Config::new().ttl(64))?;
65
66        let echo_request =
67            PayloadLengthDelimitedEchoRequest::new(Some(1.into()), Some(2.into()), b"1234");
68
69        let echo_request_bytes = echo_request.render_v4_packet_bytes();
70        client.send_to(&echo_request_bytes, (ip, 0)).await?;
71
72        let mut buf = vec![0; 1024];
73        let (n, addr_recv_from) = client.recv_from(&mut buf).await?;
74        assert_eq!(addr_recv_from, (ip, 0).into());
75
76        match Icmpv4::parse_from_packet_bytes(&buf[..n]) {
77            Ok(Some(Icmpv4::EchoReply(echo_reply))) => {
78                // TODO, why not eq
79                // assert_eq!(echo_request.identifier, echo_reply.identifier);
80                println!("{echo_reply:?}");
81            }
82            x => panic!("{x:?}"),
83        }
84
85        Ok(())
86    }
87
88    pub(crate) async fn ping_ipv6<C: AsyncClient>(
89        ip: Ipv6Addr,
90    ) -> Result<(), Box<dyn std::error::Error>> {
91        let client = C::with_config(&Config::with_ipv6().ttl(64))?;
92
93        let echo_request =
94            PayloadLengthDelimitedEchoRequest::new(Some(1.into()), Some(2.into()), b"1234");
95
96        let echo_request_bytes = echo_request.render_v6_packet_bytes();
97        client.send_to(&echo_request_bytes, (ip, 0)).await?;
98
99        let mut buf = vec![0; 1024];
100        let (n, addr_recv_from) = client.recv_from(&mut buf).await?;
101        assert_eq!(addr_recv_from, (ip, 0).into());
102
103        match Icmpv6::parse_from_packet_bytes(&buf[..n]) {
104            Ok(Some(Icmpv6::EchoReply(echo_reply))) => {
105                // TODO, why not eq
106                // assert_eq!(echo_request.identifier, echo_reply.identifier);
107                println!("{echo_reply:?}");
108            }
109            x => panic!("{x:?}"),
110        }
111
112        Ok(())
113    }
114}