#![deny(unsafe_code, missing_docs)]
use std::{fmt, io, net, ops};
pub mod message;
pub mod socket;
pub mod platform;
pub mod ping;
mod sealed {
pub trait Sealed {}
}
pub trait IcmpVersion: sealed::Sealed + fmt::Debug + Send + Sync + 'static {
type Address: Into<net::IpAddr> + Copy + Send + Sync;
type SocketAddr: Into<net::SocketAddr> + PartialEq + Eq + fmt::Debug + Copy;
const DOMAIN: socket2::Domain;
const PROTOCOL: socket2::Protocol;
const DEFAULT_BIND: Self::SocketAddr;
fn extract_icmp_from_recv_packet(packet: &[u8]) -> io::Result<(&[u8], ops::Range<usize>)>;
fn checksum_required() -> bool;
}
#[derive(Debug)]
pub struct Icmpv4;
impl sealed::Sealed for Icmpv4 {}
impl IcmpVersion for Icmpv4 {
type Address = net::Ipv4Addr;
type SocketAddr = net::SocketAddrV4;
const DOMAIN: socket2::Domain = socket2::Domain::IPV4;
const PROTOCOL: socket2::Protocol = socket2::Protocol::ICMPV4;
const DEFAULT_BIND: Self::SocketAddr = net::SocketAddrV4::new(net::Ipv4Addr::UNSPECIFIED, 0);
fn extract_icmp_from_recv_packet(packet: &[u8]) -> io::Result<(&[u8], ops::Range<usize>)> {
if platform::ipv4_recv_prefix_ipv4_header() {
socket::strip_ipv4_header(packet).map_err(|_e| {
io::Error::new(io::ErrorKind::InvalidData, "Could not strip IPv4 header")
})
} else {
Ok((packet, 0..packet.len()))
}
}
fn checksum_required() -> bool {
platform::ipv4_send_checksum_required()
}
}
#[derive(Debug)]
pub struct Icmpv6;
impl sealed::Sealed for Icmpv6 {}
impl IcmpVersion for Icmpv6 {
type Address = net::Ipv6Addr;
type SocketAddr = net::SocketAddrV6;
const DOMAIN: socket2::Domain = socket2::Domain::IPV6;
const PROTOCOL: socket2::Protocol = socket2::Protocol::ICMPV6;
const DEFAULT_BIND: Self::SocketAddr =
net::SocketAddrV6::new(net::Ipv6Addr::UNSPECIFIED, 0, 0, 0);
fn extract_icmp_from_recv_packet(packet: &[u8]) -> io::Result<(&[u8], ops::Range<usize>)> {
Ok((packet, 0..packet.len()))
}
fn checksum_required() -> bool {
false
}
}
#[allow(missing_docs)]
#[derive(Debug, Eq, PartialEq, Clone, Copy)]
pub enum IpVersion {
V4,
V6,
}
impl fmt::Display for IpVersion {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
IpVersion::V4 => write!(f, "IPv4"),
IpVersion::V6 => write!(f, "IPv6"),
}
}
}
impl From<net::IpAddr> for IpVersion {
fn from(value: net::IpAddr) -> Self {
(&value).into()
}
}
impl From<&net::IpAddr> for IpVersion {
fn from(value: &net::IpAddr) -> Self {
match value {
net::IpAddr::V4(_) => Self::V4,
net::IpAddr::V6(_) => Self::V6,
}
}
}
impl From<net::Ipv4Addr> for IpVersion {
fn from(_value: net::Ipv4Addr) -> Self {
Self::V4
}
}
impl From<&net::Ipv4Addr> for IpVersion {
fn from(_value: &net::Ipv4Addr) -> Self {
Self::V4
}
}
impl From<net::Ipv6Addr> for IpVersion {
fn from(_value: net::Ipv6Addr) -> Self {
Self::V6
}
}
impl From<&net::Ipv6Addr> for IpVersion {
fn from(_value: &net::Ipv6Addr) -> Self {
Self::V6
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ip_addr_into_ip_version() {
let ip_addr = net::IpAddr::V4(net::Ipv4Addr::LOCALHOST);
assert_eq!(IpVersion::V4, (&ip_addr).into());
assert_eq!(IpVersion::V4, ip_addr.into());
}
#[test]
fn ipv4_addr_into_ip_version() {
let ip_addr = net::Ipv4Addr::LOCALHOST;
assert_eq!(IpVersion::V4, (&ip_addr).into());
assert_eq!(IpVersion::V4, ip_addr.into());
}
#[test]
fn ipv6_addr_into_ip_version() {
let ip_addr = net::Ipv6Addr::LOCALHOST;
assert_eq!(IpVersion::V6, (&ip_addr).into());
assert_eq!(IpVersion::V6, ip_addr.into());
}
}