1use 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#[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
39pub mod config;
41pub use config::Config;
42
43pub mod utils;
44
45#[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 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 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 println!("{echo_reply:?}");
108 }
109 x => panic!("{x:?}"),
110 }
111
112 Ok(())
113 }
114}