use super::TSocket;
use crate::details::icmp::v4::Ttl;
use pnet_packet::{ipv4::Ipv4Packet, Packet};
use socket2::{Domain, Protocol, Type};
use std::{io, time::Duration};
pub(crate) struct RawSocket {
socket: socket2::Socket,
}
impl RawSocket {
pub(crate) fn new(timeout: Duration) -> Result<Self, io::Error> {
tracing::trace!("creating RawSocket");
let socket = socket2::Socket::new(Domain::IPV4, Type::RAW, Some(Protocol::ICMPV4))?;
socket.set_read_timeout(Some(timeout)).expect("could not set socket timeout");
Ok(RawSocket { socket })
}
}
impl TSocket for RawSocket {
fn send_to(&self, buf: &[u8], addr: &socket2::SockAddr) -> io::Result<usize> {
self.socket.send_to(buf, addr)
}
fn recv_from(&self, buf: &mut [u8]) -> io::Result<(usize, std::net::IpAddr, Ttl)> {
let mut recv_buf = [0u8; 256];
let (_, socket_addr) = socket2::Socket::recv_from(&self.socket, unsafe {
&mut *(std::ptr::addr_of_mut!(recv_buf) as *mut [u8] as *mut [std::mem::MaybeUninit<u8>])
})?;
let ipv4_packet = Ipv4Packet::new(&recv_buf).expect("could not initialize IPv4 package");
let ip_payload: &[u8] = ipv4_packet.payload();
if ip_payload.len() > buf.len() {
return Err(io::Error::new(io::ErrorKind::Other, "recveive buffer too small"));
}
buf[..ip_payload.len()].copy_from_slice(ip_payload);
let ip = *socket_addr.as_socket_ipv4().expect("logic error").ip();
Ok((ip_payload.len(), std::net::IpAddr::V4(ip), ipv4_packet.get_ttl().into()))
}
}