1#![deny(
20 rust_2018_idioms,
21 unreachable_pub,
22 clippy::doc_markdown,
23 rustdoc::broken_intra_doc_links
24)]
25
26#[cfg(feature = "stream")]
27use std::pin::Pin;
28use std::{
29 io,
30 marker::PhantomData,
31 net::{IpAddr, Ipv4Addr, Ipv6Addr},
32 task::{Context, Poll},
33 time::Duration,
34};
35
36#[cfg(feature = "stream")]
37use futures_core::Stream;
38
39pub use self::{
40 ip_version::IpVersion,
41 pinger::{MeasureManyStream, Pinger, V4Pinger, V6Pinger},
42};
43
44mod ip_version;
45pub mod packet;
46mod pinger;
47pub mod raw_pinger;
48mod socket;
49
50pub struct DualstackPinger {
52 v4: V4Pinger,
53 v6: V6Pinger,
54}
55
56impl DualstackPinger {
57 pub fn new() -> io::Result<Self> {
64 let v4 = V4Pinger::new()?;
65 let v6 = V6Pinger::new()?;
66 Ok(Self { v4, v6 })
67 }
68
69 pub fn measure_many<I>(&self, addresses: I) -> DualstackMeasureManyStream<'_, I>
76 where
77 I: Iterator<Item = IpAddr> + Clone,
78 {
79 let addresses_v4 = FilterIpAddr {
80 iter: addresses.clone(),
81 _marker: PhantomData,
82 };
83 let addresses_v6 = FilterIpAddr {
84 iter: addresses,
85 _marker: PhantomData,
86 };
87
88 DualstackMeasureManyStream {
89 v4: self.v4.measure_many(addresses_v4),
90 v6: self.v6.measure_many(addresses_v6),
91 v4_done: false,
92 v6_done: false,
93 }
94 }
95}
96
97pub struct DualstackMeasureManyStream<'a, I: Iterator<Item = IpAddr>> {
108 v4: MeasureManyStream<'a, Ipv4Addr, FilterIpAddr<I, Ipv4Addr>>,
109 v6: MeasureManyStream<'a, Ipv6Addr, FilterIpAddr<I, Ipv6Addr>>,
110 v4_done: bool,
111 v6_done: bool,
112}
113
114impl<I: Iterator<Item = IpAddr>> DualstackMeasureManyStream<'_, I> {
115 pub fn poll_next_unpin(&mut self, cx: &mut Context<'_>) -> Poll<Option<(IpAddr, Duration)>> {
116 if !self.v4_done {
117 match self.v4.poll_next_unpin(cx) {
118 Poll::Ready(Some((v4, rtt))) => return Poll::Ready(Some((IpAddr::V4(v4), rtt))),
119 Poll::Ready(None) => self.v4_done = true,
120 Poll::Pending => {}
121 }
122 }
123
124 if !self.v6_done {
125 match self.v6.poll_next_unpin(cx) {
126 Poll::Ready(Some((v6, rtt))) => return Poll::Ready(Some((IpAddr::V6(v6), rtt))),
127 Poll::Ready(None) => self.v6_done = true,
128 Poll::Pending => {}
129 }
130 }
131
132 if self.v4_done && self.v6_done {
133 return Poll::Ready(None);
134 }
135
136 Poll::Pending
137 }
138}
139
140#[cfg(feature = "stream")]
141impl<I: Iterator<Item = IpAddr> + Unpin> Stream for DualstackMeasureManyStream<'_, I> {
142 type Item = (IpAddr, Duration);
143
144 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
145 self.as_mut().poll_next_unpin(cx)
146 }
147}
148
149struct FilterIpAddr<I, V: IpVersion> {
150 iter: I,
151 _marker: PhantomData<V>,
152}
153
154impl<I: Iterator<Item = IpAddr>, V: IpVersion> Iterator for FilterIpAddr<I, V> {
155 type Item = V;
156
157 fn next(&mut self) -> Option<Self::Item> {
158 loop {
159 let item = self.iter.next()?;
160 if let Some(addr) = V::from_ip_addr(item) {
161 return Some(addr);
162 }
163 }
164 }
165}