async_icmp/socket/
pair.rs

1//! See [`SocketPair`] for IPv4 + IPv6 use cases.
2
3use crate::{
4    message::{echo::EchoId, EncodeIcmpMessage},
5    platform,
6    socket::{IcmpSocket, SocketConfig},
7    Icmpv4, Icmpv6, IpVersion,
8};
9use std::{io, net, ops};
10
11/// Holds both an IPv4 and IPv6 socket.
12///
13/// This is a quite simple wrapper. Access the `pub` fields to send or receive when you know the IP
14/// version to use at compile time. To send a message that supports both versions, see
15/// [`SocketPair::send_to_either`], and to receive from either socket, see
16/// [`SocketPair::recv_either`].
17pub struct SocketPair {
18    /// The IPv4 socket
19    pub ipv4: IcmpSocket<Icmpv4>,
20    /// The IPv6 socket
21    pub ipv6: IcmpSocket<Icmpv6>,
22}
23impl SocketPair {
24    /// Create a new SocketPair.
25    pub fn new(
26        icmpv4_config: SocketConfig<Icmpv4>,
27        icmpv6_config: SocketConfig<Icmpv6>,
28    ) -> io::Result<Self> {
29        Ok(Self {
30            ipv4: IcmpSocket::new(icmpv4_config)?,
31            ipv6: IcmpSocket::new(icmpv6_config)?,
32        })
33    }
34
35    /// Send `msg` to either the IPv4 or IPv6 sockets, per the IP version of `addr`.
36    pub async fn send_to_either<M>(&self, msg: &mut M, addr: net::IpAddr) -> io::Result<()>
37    where
38        M: EncodeIcmpMessage<Icmpv4> + EncodeIcmpMessage<Icmpv6>,
39    {
40        match addr {
41            net::IpAddr::V4(v4) => self.ipv4.send_to(msg, v4).await,
42            net::IpAddr::V6(v6) => self.ipv6.send_to(msg, v6).await,
43        }
44    }
45
46    /// Receive from the socket indicated by `ip_version`.
47    ///
48    /// This provides a convenient way to have a consistent `Future` type that can represent
49    /// reading from either socket.
50    pub async fn recv_either<'a>(
51        &self,
52        ip_version: IpVersion,
53        buf: &'a mut [u8],
54    ) -> io::Result<(&'a [u8], ops::Range<usize>)> {
55        match ip_version {
56            IpVersion::V4 => self.ipv4.recv(buf).await,
57            IpVersion::V6 => self.ipv6.recv(buf).await,
58        }
59    }
60
61    /// Like [`IcmpSocket::platform_echo_id`], but for whichever socket matches `ip_version`.
62    pub fn platform_echo_id(&self, ip_version: IpVersion) -> Option<EchoId> {
63        if platform::icmp_send_overwrite_echo_id_with_local_port() {
64            let port = match ip_version {
65                IpVersion::V4 => self.ipv4.local_port(),
66                IpVersion::V6 => self.ipv6.local_port(),
67            };
68
69            Some(EchoId::from_be(port))
70        } else {
71            None
72        }
73    }
74}