use std::io;
use std::time::Duration;
use crate::io::raw::Raw;
pub(crate) trait Plain: Copy {}
#[cfg(unix)]
mod imp {
use super::Plain;
pub(crate) type Int = libc::c_int;
pub(crate) type Linger = libc::linger;
impl Plain for Int {}
impl Plain for Linger {}
pub(crate) const SOL_SOCKET: Int = libc::SOL_SOCKET;
pub(crate) const SO_KEEPALIVE: Int = libc::SO_KEEPALIVE;
pub(crate) const SO_REUSEADDR: Int = libc::SO_REUSEADDR;
#[cfg(not(any(target_os = "solaris", target_os = "illumos")))]
pub(crate) const SO_REUSEPORT: Int = libc::SO_REUSEPORT;
pub(crate) const SO_SNDBUF: Int = libc::SO_SNDBUF;
pub(crate) const SO_RCVBUF: Int = libc::SO_RCVBUF;
pub(crate) const SO_LINGER: Int = libc::SO_LINGER;
pub(crate) const IPPROTO_IP: Int = libc::IPPROTO_IP;
pub(crate) const IP_TOS: Int = libc::IP_TOS;
pub(crate) const IPPROTO_IPV6: Int = libc::IPPROTO_IPV6;
pub(crate) const IPV6_TCLASS: Int = libc::IPV6_TCLASS;
pub(crate) fn linger(on: bool, secs: u64) -> Linger {
libc::linger {
l_onoff: Int::from(on),
l_linger: Int::try_from(secs).unwrap_or(Int::MAX),
}
}
pub(crate) fn linger_of(linger: Linger) -> Option<u64> {
(linger.l_onoff != 0).then(|| u64::try_from(linger.l_linger).unwrap_or(0))
}
pub(crate) unsafe fn get_raw(
raw: super::Raw,
level: Int,
name: Int,
value: *mut u8,
len: usize,
) -> std::io::Result<usize> {
let mut len = len as libc::socklen_t;
let r = unsafe { libc::getsockopt(raw, level, name, value.cast(), &mut len) };
if r < 0 {
return Err(std::io::Error::last_os_error());
}
Ok(len as usize)
}
pub(crate) unsafe fn set_raw(
raw: super::Raw,
level: Int,
name: Int,
value: *const u8,
len: usize,
) -> std::io::Result<()> {
let r = unsafe { libc::setsockopt(raw, level, name, value.cast(), len as libc::socklen_t) };
if r < 0 {
return Err(std::io::Error::last_os_error());
}
Ok(())
}
}
#[cfg(windows)]
mod imp {
use super::Plain;
use crate::syscore::windows as win;
pub(crate) type Int = i32;
pub(crate) type Linger = win::LINGER;
impl Plain for Int {}
impl Plain for Linger {}
pub(crate) use win::{
IP_TOS, IPPROTO_IP, SO_KEEPALIVE, SO_LINGER, SO_RCVBUF, SO_REUSEADDR, SO_SNDBUF, SOL_SOCKET,
};
pub(crate) fn linger(on: bool, secs: u64) -> Linger {
win::LINGER {
l_onoff: u16::from(on),
l_linger: u16::try_from(secs).unwrap_or(u16::MAX),
}
}
pub(crate) fn linger_of(linger: Linger) -> Option<u64> {
(linger.l_onoff != 0).then_some(u64::from(linger.l_linger))
}
pub(crate) unsafe fn get_raw(
raw: super::Raw,
level: Int,
name: Int,
value: *mut u8,
len: usize,
) -> std::io::Result<usize> {
let mut len = len as i32;
if unsafe { win::getsockopt(raw as win::SOCKET, level, name, value, &mut len) } != 0 {
return Err(win::wsa_error());
}
Ok(len as usize)
}
pub(crate) unsafe fn set_raw(
raw: super::Raw,
level: Int,
name: Int,
value: *const u8,
len: usize,
) -> std::io::Result<()> {
if unsafe { win::setsockopt(raw as win::SOCKET, level, name, value, len as i32) } != 0 {
return Err(win::wsa_error());
}
Ok(())
}
}
pub(crate) use imp::*;
pub(crate) fn get<T: Plain>(raw: Raw, level: Int, name: Int) -> io::Result<T> {
let mut value = core::mem::MaybeUninit::<T>::zeroed();
unsafe {
get_raw(raw, level, name, value.as_mut_ptr().cast(), size_of::<T>())?;
Ok(value.assume_init())
}
}
pub(crate) fn set<T: Plain>(raw: Raw, level: Int, name: Int, value: T) -> io::Result<()> {
unsafe {
set_raw(
raw,
level,
name,
core::ptr::from_ref(&value).cast(),
size_of::<T>(),
)
}
}
pub(crate) fn flag(raw: Raw, level: Int, name: Int) -> io::Result<bool> {
get::<Int>(raw, level, name).map(|v| v != 0)
}
pub(crate) fn set_flag(raw: Raw, level: Int, name: Int, on: bool) -> io::Result<()> {
set(raw, level, name, Int::from(on))
}
pub(crate) fn size(raw: Raw, level: Int, name: Int) -> io::Result<u32> {
get::<Int>(raw, level, name).map(|v| u32::try_from(v).unwrap_or(0))
}
pub(crate) fn set_size(raw: Raw, level: Int, name: Int, bytes: u32) -> io::Result<()> {
set(raw, level, name, Int::try_from(bytes).unwrap_or(Int::MAX))
}
pub(crate) fn linger_time(raw: Raw) -> io::Result<Option<Duration>> {
get::<Linger>(raw, SOL_SOCKET, SO_LINGER).map(|l| linger_of(l).map(Duration::from_secs))
}
pub(crate) fn set_linger_time(raw: Raw, time: Option<Duration>) -> io::Result<()> {
let value = match time {
Some(time) => linger(true, time.as_secs()),
None => linger(false, 0),
};
set(raw, SOL_SOCKET, SO_LINGER, value)
}