use std::convert::TryInto;
use std::io::IoSlice;
use std::ptr::NonNull;
use crate::{
capture::{Active, Capture},
raw, Error,
};
pub struct SendQueue(NonNull<raw::pcap_send_queue>);
pub enum SendSync {
Off = 0,
On = 1,
}
#[inline]
fn make_pkthdr(ts: Option<std::time::Duration>, len: u32) -> raw::pcap_pkthdr {
raw::pcap_pkthdr {
ts: if let Some(ts) = ts {
libc::timeval {
tv_sec: ts.as_secs() as i32,
tv_usec: ts.subsec_micros() as i32,
}
} else {
libc::timeval {
tv_sec: 0,
tv_usec: 0,
}
},
caplen: len,
len,
}
}
impl SendQueue {
pub fn new(memsize: u32) -> Result<Self, Error> {
let squeue = unsafe { raw::pcap_sendqueue_alloc(memsize) };
let squeue = NonNull::new(squeue).ok_or(Error::InsufficientMemory)?;
Ok(Self(squeue))
}
pub fn maxlen(&self) -> u32 {
unsafe { self.0.as_ref().maxlen }
}
pub fn is_empty(&self) -> bool {
self.len() == 0
}
pub fn len(&self) -> u32 {
unsafe { self.0.as_ref().len }
}
pub fn queue(&mut self, ts: Option<std::time::Duration>, buf: &[u8]) -> Result<(), Error> {
let len = buf.len().try_into().ok().ok_or(Error::BufferOverflow)?;
let pkthdr = make_pkthdr(ts, len);
let ph = &pkthdr as *const _;
let res = unsafe { raw::pcap_sendqueue_queue(self.0.as_ptr(), ph, buf.as_ptr()) };
if res == -1 {
return Err(Error::InsufficientMemory);
}
Ok(())
}
pub fn queue_sg(
&mut self,
ts: Option<std::time::Duration>,
iov: &[IoSlice<'_>],
) -> Result<(), Error> {
let pktsize: usize = iov.iter().map(|b| b.len()).sum();
let remain = (self.maxlen() - self.len()) as usize;
let need = std::mem::size_of::<raw::pcap_pkthdr>() + pktsize;
if remain < need {
return Err(Error::BufferOverflow);
}
let pktlen = pktsize.try_into().ok().ok_or(Error::BufferOverflow)?;
let pkthdr = make_pkthdr(ts, pktlen);
let rawhdr = &pkthdr as *const _ as *const u8;
let rawsq = unsafe { self.0.as_mut() };
let sqbuf = rawsq.buffer as *mut u8;
let bufoffs = rawsq.len.try_into().ok().ok_or(Error::BufferOverflow)?;
let mut wbuf = unsafe { sqbuf.offset(bufoffs) };
let mut lastlen = std::mem::size_of::<raw::pcap_pkthdr>();
unsafe {
std::ptr::copy_nonoverlapping(rawhdr, wbuf, lastlen);
}
for b in iov {
let len = lastlen.try_into().ok().ok_or(Error::BufferOverflow)?;
wbuf = unsafe { wbuf.offset(len) };
unsafe {
std::ptr::copy_nonoverlapping(b.as_ptr(), wbuf, b.len());
}
lastlen = b.len();
}
rawsq.len += need as u32;
Ok(())
}
pub fn transmit(&mut self, dev: &mut Capture<Active>, sync: SendSync) -> Result<(), Error> {
let res =
unsafe { raw::pcap_sendqueue_transmit(dev.as_ptr(), self.0.as_ptr(), sync as i32) };
if res < self.len() {
return unsafe { Err(Error::new(raw::pcap_geterr(dev.as_ptr()))) };
} else {
self.reset();
}
Ok(())
}
pub fn reset(&mut self) {
unsafe { self.0.as_mut() }.len = 0;
}
}
impl Drop for SendQueue {
fn drop(&mut self) {
unsafe {
raw::pcap_sendqueue_destroy(self.0.as_ptr());
}
}
}