use libc::{
self, __errno_location, c_int, close, recvfrom, socket, AF_INET, EACCES, EFAULT, EINVAL,
EMSGSIZE, EAGAIN, EOPNOTSUPP, EPERM, EPROTO, IPPROTO_TCP, SOCK_RAW, SOCK_NONBLOCK,
};
use thiserror::Error;
use std::convert::From;
use std::io::Read;
use std::io::Result as IOResult;
use std::io::{Error, ErrorKind};
use std::time::{Duration, Instant};
#[derive(Error, Debug)]
pub enum RSockErr {
#[error("Tried to send broadcast address without broadcast flag")]
Eaccess,
#[error("Invalid memory address supplied")]
Efault,
#[error("Invalid argument passed")]
Einval,
#[error("Packet too large")]
Emsgsize,
#[error("Invalid flag passed to socket call")]
Eopnotsupp,
#[error("User doesn't have permissions for operation")]
Eperm,
#[error("ICMP error")]
Eproto,
#[error("Try operation again")]
Eagain,
#[error("Got errno {0}")]
Errno(isize),
}
impl From<c_int> for RSockErr {
fn from(val: c_int) -> Self {
match val {
EACCES => Self::Eaccess,
EFAULT => Self::Efault,
EINVAL => Self::Einval,
EMSGSIZE => Self::Emsgsize,
EOPNOTSUPP => Self::Eopnotsupp,
EPERM => Self::Eperm,
EPROTO => Self::Eproto,
EAGAIN => Self::Eagain,
x => Self::Errno(x as isize),
}
}
}
#[derive(Debug, Error)]
pub enum TimeoutRead {
#[error("Failed to read from socket")]
IO(#[from] RSockErr),
#[error("No bytes were returned before timeout")]
Timeout,
}
pub struct Raw(c_int);
impl Raw {
pub fn new() -> Result<Self, RSockErr> {
let fd = unsafe { socket(AF_INET, SOCK_RAW | SOCK_NONBLOCK, IPPROTO_TCP) };
if fd < 0 {
let errno = unsafe { *__errno_location() };
Err(errno.into())
} else {
Ok(Raw(fd))
}
}
pub fn bind_interface(&mut self, interface: &dyn AsRef<str>) -> Result<(), RSockErr> {
let interface = interface.as_ref();
if interface.len() > libc::IFNAMSIZ {
return Err(RSockErr::Einval);
}
let iface = interface.as_ptr();
let ret = unsafe {
libc::setsockopt(
self.0,
libc::SOL_SOCKET,
libc::SO_BINDTODEVICE,
iface.cast(),
interface.len() as u32,
)
};
if ret < 0 {
let errno = unsafe { *__errno_location() };
return Err(errno.into());
}
Ok(())
}
pub fn read_timeout(&mut self, buf: &mut [u8], timeout: &Duration) -> Result<usize, TimeoutRead> {
let start = Instant::now();
'try_loop: loop {
let read = self.read(buf);
match read {
Ok(n) => return Ok(n),
Err(e) => {
let inner = unsafe { e.into_inner().unwrap_unchecked() };
let sock_err = unsafe { inner.downcast::<RSockErr>().unwrap_unchecked() };
match *sock_err {
RSockErr::Eagain => {},
e => return Err(TimeoutRead::IO(e)),
};
}
};
let elapsed = start.elapsed();
if elapsed > *timeout {
break 'try_loop;
}
}
Err(TimeoutRead::Timeout)
}
}
impl Drop for Raw {
fn drop(&mut self) {
unsafe {
close(self.0);
}
}
}
impl Read for Raw {
fn read(&mut self, buf: &mut [u8]) -> IOResult<usize> {
let addr = buf.as_mut_ptr();
let ret = unsafe { recvfrom(self.0, addr.cast(), buf.len(), 0, 0 as _, 0 as _) };
if ret > 0 {
Ok(ret as usize)
} else {
let errno = unsafe { *__errno_location() };
let err: RSockErr = errno.into();
Err(Error::new(ErrorKind::Other, err))
}
}
}