async-icmp 0.2.1

Async ICMP library
Documentation
//! See [`SocketPair`] for IPv4 + IPv6 use cases.

use crate::{
    message::{echo::EchoId, EncodeIcmpMessage},
    platform,
    socket::{IcmpSocket, SocketConfig},
    Icmpv4, Icmpv6, IpVersion,
};
use std::{io, net, ops};

/// Holds both an IPv4 and IPv6 socket.
///
/// This is a quite simple wrapper. Access the `pub` fields to send or receive when you know the IP
/// version to use at compile time. To send a message that supports both versions, see
/// [`SocketPair::send_to_either`], and to receive from either socket, see
/// [`SocketPair::recv_either`].
pub struct SocketPair {
    /// The IPv4 socket
    pub ipv4: IcmpSocket<Icmpv4>,
    /// The IPv6 socket
    pub ipv6: IcmpSocket<Icmpv6>,
}
impl SocketPair {
    /// Create a new SocketPair.
    pub fn new(
        icmpv4_config: SocketConfig<Icmpv4>,
        icmpv6_config: SocketConfig<Icmpv6>,
    ) -> io::Result<Self> {
        Ok(Self {
            ipv4: IcmpSocket::new(icmpv4_config)?,
            ipv6: IcmpSocket::new(icmpv6_config)?,
        })
    }

    /// Send `msg` to either the IPv4 or IPv6 sockets, per the IP version of `addr`.
    pub async fn send_to_either<M>(&self, msg: &mut M, addr: net::IpAddr) -> io::Result<()>
    where
        M: EncodeIcmpMessage<Icmpv4> + EncodeIcmpMessage<Icmpv6>,
    {
        match addr {
            net::IpAddr::V4(v4) => self.ipv4.send_to(msg, v4).await,
            net::IpAddr::V6(v6) => self.ipv6.send_to(msg, v6).await,
        }
    }

    /// Receive from the socket indicated by `ip_version`.
    ///
    /// This provides a convenient way to have a consistent `Future` type that can represent
    /// reading from either socket.
    pub async fn recv_either<'a>(
        &self,
        ip_version: IpVersion,
        buf: &'a mut [u8],
    ) -> io::Result<(&'a [u8], ops::Range<usize>)> {
        match ip_version {
            IpVersion::V4 => self.ipv4.recv(buf).await,
            IpVersion::V6 => self.ipv6.recv(buf).await,
        }
    }

    /// Like [`IcmpSocket::platform_echo_id`], but for whichever socket matches `ip_version`.
    pub fn platform_echo_id(&self, ip_version: IpVersion) -> Option<EchoId> {
        if platform::icmp_send_overwrite_echo_id_with_local_port() {
            let port = match ip_version {
                IpVersion::V4 => self.ipv4.local_port(),
                IpVersion::V6 => self.ipv6.local_port(),
            };

            Some(EchoId::from_be(port))
        } else {
            None
        }
    }
}