use std::time::Duration;
use std::{cmp, io, ptr, thread};
use windows_sys::Win32::Foundation::{ERROR_NO_MORE_ITEMS, HANDLE};
use windows_sys::Win32::System::Threading::{WaitForSingleObject, INFINITE};
use super::dll::WintunSession;
use super::TunAdapter;
pub struct TunSession<'a> {
adapter: &'a TunAdapter,
session: *mut WintunSession,
nonblocking: bool,
}
impl<'a> TunSession<'a> {
pub const DEFAULT_RING_SIZE: u32 = 0x200000;
const SEND_MAX_BLOCKING_INTERVAL: u64 = 100;
pub(crate) fn new(adapter: &'a TunAdapter, session: *mut WintunSession) -> Self {
Self {
adapter,
session,
nonblocking: false,
}
}
#[inline]
pub fn read_handle(&self) -> HANDLE {
Self::read_handle_impl(&self.adapter, self.session)
}
pub(crate) fn read_handle_impl(adapter: &TunAdapter, session: *mut WintunSession) -> HANDLE {
adapter.wintun.read_event_handle(session)
}
#[inline]
pub fn send(&self, buf: &[u8]) -> io::Result<usize> {
Self::send_impl(&self.adapter, self.session, self.nonblocking, buf)
}
pub(crate) fn send_impl(
adapter: &TunAdapter,
session: *mut WintunSession,
nonblocking: bool,
buf: &[u8],
) -> io::Result<usize> {
let packet_size = cmp::min(buf.len(), u16::MAX as usize);
let mut timeout = 1; let pkt = loop {
let pkt_res = adapter.wintun.allocate_packet(session, packet_size as u32);
if nonblocking {
break pkt_res.map_err(|_| io::Error::from(io::ErrorKind::WouldBlock))?;
} else {
match pkt_res {
Ok(pkt) => break pkt,
Err(_) => timeout = cmp::min(timeout * 2, Self::SEND_MAX_BLOCKING_INTERVAL),
}
}
thread::sleep(Duration::from_millis(timeout));
};
unsafe { ptr::copy_nonoverlapping(buf.as_ptr(), pkt.as_ptr(), packet_size) };
adapter.wintun.send_packet(session, pkt);
Ok(packet_size)
}
#[inline]
pub fn recv(&self, buf: &mut [u8]) -> io::Result<usize> {
Self::recv_impl(&self.adapter, self.session, self.nonblocking, buf)
}
pub(crate) fn recv_impl(
adapter: &TunAdapter,
session: *mut WintunSession,
nonblocking: bool,
buf: &mut [u8],
) -> io::Result<usize> {
let mut packet_size = 0u32;
let recv_pkt = match adapter.wintun.recv_packet(session, &mut packet_size) {
Ok(pkt) => pkt,
Err(e) if e.raw_os_error() == Some(ERROR_NO_MORE_ITEMS as i32) && nonblocking => {
return Err(io::ErrorKind::WouldBlock.into())
}
Err(e) if e.raw_os_error() == Some(ERROR_NO_MORE_ITEMS as i32) => loop {
let read_handle = adapter.wintun.read_event_handle(session);
unsafe { WaitForSingleObject(read_handle, INFINITE) };
if let Ok(pkt) = adapter.wintun.recv_packet(session, &mut packet_size) {
break pkt;
}
},
Err(e) => return Err(e),
};
let output_size = cmp::min(packet_size as usize, buf.len());
unsafe {
ptr::copy_nonoverlapping(recv_pkt.as_ptr(), buf.as_mut_ptr(), output_size);
}
adapter.wintun.free_packet(session, recv_pkt);
Ok(output_size)
}
pub fn nonblocking(&self) -> bool {
self.nonblocking
}
pub fn set_nonblocking(&mut self, nonblocking: bool) {
self.nonblocking = nonblocking;
}
}
impl Drop for TunSession<'_> {
fn drop(&mut self) {
unsafe {
self.adapter.wintun.end_session(self.session);
}
}
}
unsafe impl Send for TunSession<'_> {}
unsafe impl Sync for TunSession<'_> {}