use crate::{
message::{EncodeIcmpMessage, IcmpMessageBuffer, IcmpV4MsgType, IcmpV6MsgType},
Icmpv4, Icmpv6,
};
#[cfg(feature = "rand")]
use rand::Rng;
use std::fmt;
use winnow::{combinator, token, Parser};
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct EchoId([u8; 2]);
impl EchoId {
pub fn from_be(num: u16) -> Self {
Self(num.to_be_bytes())
}
pub fn from_le(num: u16) -> Self {
Self(num.to_le_bytes())
}
pub fn as_array(&self) -> [u8; 2] {
self.0
}
pub fn as_slice(&self) -> &[u8] {
&self.0
}
pub fn as_be(&self) -> u16 {
u16::from_be_bytes(self.0)
}
pub fn as_le(&self) -> u16 {
u16::from_le_bytes(self.0)
}
}
impl From<[u8; 2]> for EchoId {
fn from(value: [u8; 2]) -> Self {
Self(value)
}
}
impl fmt::Debug for EchoId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "0x{}", hex::encode_upper(self.0))
}
}
#[cfg(feature = "rand")]
impl rand::distributions::Distribution<EchoId> for rand::distributions::Standard {
fn sample<R: Rng + ?Sized>(&self, rng: &mut R) -> EchoId {
EchoId::from_be(rng.gen())
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct EchoSeq([u8; 2]);
impl EchoSeq {
pub fn from_be(num: u16) -> Self {
Self(num.to_be_bytes())
}
pub fn from_le(num: u16) -> Self {
Self(num.to_le_bytes())
}
pub fn as_array(&self) -> [u8; 2] {
self.0
}
pub fn as_slice(&self) -> &[u8] {
&self.0
}
pub fn as_be(&self) -> u16 {
u16::from_be_bytes(self.0)
}
pub fn as_le(&self) -> u16 {
u16::from_le_bytes(self.0)
}
}
impl From<[u8; 2]> for EchoSeq {
fn from(value: [u8; 2]) -> Self {
Self(value)
}
}
impl fmt::Debug for EchoSeq {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "0x{}", hex::encode_upper(self.0))
}
}
#[derive(Clone, Debug)]
pub struct IcmpEchoRequest {
buf: IcmpMessageBuffer,
}
impl IcmpEchoRequest {
const ICMP_ECHO_HDR_LEN: usize = 4;
#[allow(clippy::new_without_default)]
pub fn new() -> Self {
Self {
buf: IcmpMessageBuffer::new(0, 0, &[0; Self::ICMP_ECHO_HDR_LEN]),
}
}
pub fn from_fields(id: EchoId, seq: EchoSeq, data: &[u8]) -> Self {
let mut req = Self::new();
req.set_id(id);
req.set_seq(seq);
req.set_data(data);
req
}
pub fn id(&self) -> EchoId {
<[u8; 2]>::try_from(&self.buf.body()[..2])
.map(EchoId::from)
.unwrap()
}
pub fn seq(&self) -> EchoSeq {
<[u8; 2]>::try_from(&self.buf.body()[2..4])
.map(EchoSeq::from)
.unwrap()
}
pub fn set_id(&mut self, id: EchoId) {
self.buf.body_mut()[..2].copy_from_slice(id.as_slice());
}
pub fn set_seq(&mut self, seq: EchoSeq) {
self.buf.body_mut()[2..4].copy_from_slice(seq.as_slice());
}
pub fn set_data(&mut self, data: &[u8]) {
self.buf.truncate_body(Self::ICMP_ECHO_HDR_LEN);
self.buf.extend_body(data.iter().cloned());
}
}
impl EncodeIcmpMessage<Icmpv4> for IcmpEchoRequest {
fn encode(&mut self) -> &mut IcmpMessageBuffer {
self.buf.set_type(IcmpV4MsgType::EchoRequest as u8);
&mut self.buf
}
}
impl EncodeIcmpMessage<Icmpv6> for IcmpEchoRequest {
fn encode(&mut self) -> &mut IcmpMessageBuffer {
self.buf.set_type(IcmpV6MsgType::EchoRequest as u8);
&mut self.buf
}
}
pub fn parse_echo_reply(icmp_reply_body: &[u8]) -> Option<(EchoId, EchoSeq, &[u8])> {
(
token::take::<_, _, winnow::error::ContextError>(2_usize)
.try_map(|slice| <[u8; 2]>::try_from(slice).map(EchoId::from)),
token::take(2_usize).try_map(|slice| <[u8; 2]>::try_from(slice).map(EchoSeq::from)),
combinator::rest,
)
.parse(icmp_reply_body)
.ok()
}