use alloc::{boxed::Box, sync::Arc};
use core::{any::Any, fmt, time::Duration};
use enum_dispatch::enum_dispatch;
use crate::{InterfaceId, NetError, NetResult};
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum TcpState {
#[default]
Closed,
Listen,
SynSent,
SynReceived,
Established,
FinWait1,
FinWait2,
CloseWait,
Closing,
LastAck,
TimeWait,
}
bitflags::bitflags! {
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct TcpInfoOptions: u8 {
const TIMESTAMPS = 1 << 0;
const SACK = 1 << 1;
const WSCALE = 1 << 2;
const ECN = 1 << 3;
const ECN_SEEN = 1 << 4;
const SYN_DATA = 1 << 5;
}
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub struct TcpInfo {
pub state: TcpState,
pub ca_state: u8,
pub retransmits: u8,
pub probes: u8,
pub backoff: u8,
pub options: TcpInfoOptions,
pub snd_wscale: u8,
pub rcv_wscale: u8,
pub rto_micros: u32,
pub ato_micros: u32,
pub snd_mss: u32,
pub rcv_mss: u32,
pub notsent_bytes: u32,
pub pmtu: u32,
pub advmss: u32,
pub snd_cwnd: u32,
pub reordering: u32,
pub rcv_space: u32,
pub snd_wnd: u32,
pub rcv_wnd: u32,
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum TcpCongestionControl {
#[default]
None,
}
macro_rules! define_options {
($($name:ident($value:ty),)*) => {
#[allow(missing_docs)]
pub enum GetSocketOption<'a> {
$(
$name(&'a mut $value),
)*
}
#[allow(missing_docs)]
#[derive(Clone, Copy)]
pub enum SetSocketOption<'a> {
$(
$name(&'a $value),
)*
}
};
}
#[derive(Default, Clone)]
pub struct UnixCredentials {
pub pid: u32,
pub uid: u32,
pub gid: u32,
identity: Option<Arc<dyn Any + Send + Sync>>,
}
impl UnixCredentials {
pub fn new(pid: u32) -> Self {
UnixCredentials {
pid,
uid: 0,
gid: 0,
identity: None,
}
}
pub fn from_parts(pid: u32, uid: u32, gid: u32) -> Self {
Self {
pid,
uid,
gid,
identity: None,
}
}
pub fn with_identity<T>(mut self, identity: Arc<T>) -> Self
where
T: Any + Send + Sync,
{
self.identity = Some(identity);
self
}
pub fn identity<T: Any + Send + Sync>(&self) -> Option<&T> {
self.identity.as_deref()?.downcast_ref()
}
}
impl fmt::Debug for UnixCredentials {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter
.debug_struct("UnixCredentials")
.field("pid", &self.pid)
.field("uid", &self.uid)
.field("gid", &self.gid)
.field("has_identity", &self.identity.is_some())
.finish()
}
}
impl PartialEq for UnixCredentials {
fn eq(&self, other: &Self) -> bool {
self.pid == other.pid && self.uid == other.uid && self.gid == other.gid
}
}
impl Eq for UnixCredentials {}
impl From<u32> for UnixCredentials {
fn from(pid: u32) -> Self {
Self::new(pid)
}
}
define_options! {
ReuseAddress(bool),
ReusePort(bool),
Error(i32),
DontRoute(bool),
SendBuffer(usize),
ReceiveBuffer(usize),
KeepAlive(bool),
SendTimeout(Duration),
ReceiveTimeout(Duration),
SendBufferForce(usize),
PassCredentials(bool),
ReceiveTimestamp(bool),
PeerCredentials(UnixCredentials),
SocketType(i32),
SocketProtocol(i32),
SocketDomain(i32),
BindToDevice(Option<InterfaceId>),
Priority(i32),
NoDelay(bool),
MaxSegment(usize),
TcpKeepIdle(u32),
TcpKeepInterval(u32),
TcpKeepCount(u32),
TcpUserTimeout(u32),
TcpInfo(TcpInfo),
TcpCongestionControl(TcpCongestionControl),
Ttl(u8),
IpTos(u8),
RecvTtl(bool),
RecvTos(bool),
RecvTrafficClass(bool),
RecvErr(bool),
IpMtuDiscover(u8),
NonBlocking(bool),
}
#[enum_dispatch]
pub trait Configurable {
fn get_option_inner(&self, opt: &mut GetSocketOption) -> NetResult<bool>;
fn set_option_inner(&self, opt: SetSocketOption) -> NetResult<bool>;
fn get_option(&self, mut opt: GetSocketOption) -> NetResult {
self.get_option_inner(&mut opt).and_then(|supported| {
if !supported {
Err(NetError::ProtocolOptionUnsupported)
} else {
Ok(())
}
})
}
fn set_option(&self, opt: SetSocketOption) -> NetResult {
self.set_option_inner(opt).and_then(|supported| {
if !supported {
Err(NetError::ProtocolOptionUnsupported)
} else {
Ok(())
}
})
}
}
impl<T: Configurable + ?Sized> Configurable for Box<T> {
fn get_option_inner(&self, opt: &mut GetSocketOption) -> NetResult<bool> {
self.as_ref().get_option_inner(opt)
}
fn set_option_inner(&self, opt: SetSocketOption) -> NetResult<bool> {
self.as_ref().set_option_inner(opt)
}
}