use super::{Kind, invalid, unsupported};
use crate::{
AcceptDefaults, Error, KeepAlive, MulticastGroup, Result, SocketOption, SocketOptionKind,
};
use std::{mem::size_of, net::IpAddr, ptr, time::Duration};
use windows_sys::Win32::Networking::WinSock::*;
fn set_int(socket: usize, level: i32, name: i32, value: i32) -> Result<()> {
super::socket::check(unsafe {
setsockopt(
socket,
level,
name,
ptr::from_ref(&value).cast(),
size_of::<i32>() as i32,
)
})
}
fn get_int(socket: usize, level: i32, name: i32) -> Result<i32> {
let mut value = 0i32;
let mut len = size_of::<i32>() as i32;
super::socket::check(unsafe {
getsockopt(
socket,
level,
name,
ptr::from_mut(&mut value).cast(),
&mut len,
)
})?;
Ok(value)
}
fn bounded(value: u32) -> Result<i32> {
i32::try_from(value).map_err(|_| invalid())
}
fn whole_seconds(value: Duration) -> Result<u32> {
let seconds = value
.as_secs()
.checked_add(u64::from(value.subsec_nanos() != 0))
.ok_or_else(invalid)?;
u32::try_from(seconds).map_err(|_| invalid())
}
fn positive_seconds(value: Duration) -> Result<u32> {
match whole_seconds(value)? {
0 => Err(invalid()),
seconds => Ok(seconds),
}
}
fn ipv6(socket: usize) -> Result<bool> {
let mut storage: SOCKADDR_STORAGE = unsafe { std::mem::zeroed() };
let mut len = size_of::<SOCKADDR_STORAGE>() as i32;
super::socket::check(unsafe {
getsockname(socket, ptr::from_mut(&mut storage).cast(), &mut len)
})?;
match storage.ss_family {
AF_INET => Ok(false),
AF_INET6 => Ok(true),
_ => Err(unsupported()),
}
}
fn ip_level(socket: usize, v4: i32, v6: i32) -> Result<(i32, i32)> {
if ipv6(socket)? {
Ok((IPPROTO_IPV6, v6))
} else {
Ok((IPPROTO_IP, v4))
}
}
fn set_linger(socket: usize, value: Option<Duration>) -> Result<()> {
let seconds = value.map_or(Ok(0), whole_seconds)?;
let value = LINGER {
l_onoff: u16::from(value.is_some()),
l_linger: u16::try_from(seconds).map_err(|_| invalid())?,
};
super::socket::check(unsafe {
setsockopt(
socket,
SOL_SOCKET,
SO_LINGER,
ptr::from_ref(&value).cast(),
size_of::<LINGER>() as i32,
)
})
}
fn get_linger(socket: usize) -> Result<Option<Duration>> {
let mut value = LINGER::default();
let mut len = size_of::<LINGER>() as i32;
super::socket::check(unsafe {
getsockopt(
socket,
SOL_SOCKET,
SO_LINGER,
ptr::from_mut(&mut value).cast(),
&mut len,
)
})?;
Ok((value.l_onoff != 0).then(|| Duration::from_secs(u64::from(value.l_linger))))
}
fn set_keep_alive(socket: usize, value: Option<KeepAlive>) -> Result<()> {
let Some(schedule) = value else {
return set_int(socket, SOL_SOCKET, SO_KEEPALIVE, 0);
};
let idle = schedule.idle.map(positive_seconds).transpose()?;
let interval = schedule.interval.map(positive_seconds).transpose()?;
let count = schedule.count.map(bounded).transpose()?;
if count == Some(0) {
return Err(invalid());
}
set_int(socket, SOL_SOCKET, SO_KEEPALIVE, 1)?;
if let Some(idle) = idle {
set_int(socket, IPPROTO_TCP, TCP_KEEPIDLE, bounded(idle)?)?;
}
if let Some(interval) = interval {
set_int(socket, IPPROTO_TCP, TCP_KEEPINTVL, bounded(interval)?)?;
}
if let Some(count) = count {
set_int(socket, IPPROTO_TCP, TCP_KEEPCNT, count)?;
}
Ok(())
}
fn get_keep_alive(socket: usize) -> Result<Option<KeepAlive>> {
if get_int(socket, SOL_SOCKET, SO_KEEPALIVE)? == 0 {
return Ok(None);
}
Ok(Some(KeepAlive {
idle: Some(Duration::from_secs(
get_int(socket, IPPROTO_TCP, TCP_KEEPIDLE)?.max(0) as u64,
)),
interval: Some(Duration::from_secs(
get_int(socket, IPPROTO_TCP, TCP_KEEPINTVL)?.max(0) as u64,
)),
count: Some(get_int(socket, IPPROTO_TCP, TCP_KEEPCNT)?.max(0) as u32),
}))
}
fn membership(socket: usize, group: MulticastGroup, join: bool) -> Result<()> {
match (group.group, ipv6(socket)?) {
(IpAddr::V6(address), true) => {
let mut request = IPV6_MREQ::default();
request.ipv6mr_multiaddr.u.Byte = address.octets();
request.ipv6mr_interface = group.interface;
let name = if join {
IPV6_ADD_MEMBERSHIP
} else {
IPV6_DROP_MEMBERSHIP
};
super::socket::check(unsafe {
setsockopt(
socket,
IPPROTO_IPV6,
name,
ptr::from_ref(&request).cast(),
size_of::<IPV6_MREQ>() as i32,
)
})
}
(IpAddr::V4(_), false) if group.interface != 0 => Err(unsupported()),
(IpAddr::V4(address), false) => {
let mut request = IP_MREQ::default();
request.imr_multiaddr.S_un.S_addr = u32::from_ne_bytes(address.octets());
request.imr_interface.S_un.S_addr = 0;
let name = if join {
IP_ADD_MEMBERSHIP
} else {
IP_DROP_MEMBERSHIP
};
super::socket::check(unsafe {
setsockopt(
socket,
IPPROTO_IP,
name,
ptr::from_ref(&request).cast(),
size_of::<IP_MREQ>() as i32,
)
})
}
_ => Err(invalid()),
}
}
pub(super) fn set(socket: usize, option: SocketOption) -> Result<()> {
match option {
SocketOption::NoDelay(on) => set_int(socket, IPPROTO_TCP, TCP_NODELAY, i32::from(on)),
SocketOption::KeepAlive(schedule) => set_keep_alive(socket, schedule),
SocketOption::Linger(value) => set_linger(socket, value),
SocketOption::RecvBufferSize(bytes) => {
set_int(socket, SOL_SOCKET, SO_RCVBUF, bounded(bytes)?)
}
SocketOption::SendBufferSize(bytes) => {
set_int(socket, SOL_SOCKET, SO_SNDBUF, bounded(bytes)?)
}
SocketOption::Ttl(hops) => {
let (level, name) = ip_level(socket, IP_TTL, IPV6_UNICAST_HOPS)?;
set_int(socket, level, name, bounded(hops)?)
}
SocketOption::Ipv6Only(on) => set_int(socket, IPPROTO_IPV6, IPV6_V6ONLY, i32::from(on)),
SocketOption::Broadcast(on) => set_int(socket, SOL_SOCKET, SO_BROADCAST, i32::from(on)),
SocketOption::MulticastTtl(hops) => {
let (level, name) = ip_level(socket, IP_MULTICAST_TTL, IPV6_MULTICAST_HOPS)?;
set_int(socket, level, name, bounded(hops)?)
}
SocketOption::MulticastLoop(on) => {
let (level, name) = ip_level(socket, IP_MULTICAST_LOOP, IPV6_MULTICAST_LOOP)?;
set_int(socket, level, name, i32::from(on))
}
SocketOption::MulticastJoin(group) => membership(socket, group, true),
SocketOption::MulticastLeave(group) => membership(socket, group, false),
}
}
pub(super) fn get(socket: usize, kind: SocketOptionKind) -> Result<SocketOption> {
Ok(match kind {
SocketOptionKind::NoDelay => {
SocketOption::NoDelay(get_int(socket, IPPROTO_TCP, TCP_NODELAY)? != 0)
}
SocketOptionKind::KeepAlive => SocketOption::KeepAlive(get_keep_alive(socket)?),
SocketOptionKind::Linger => SocketOption::Linger(get_linger(socket)?),
SocketOptionKind::RecvBufferSize => {
SocketOption::RecvBufferSize(get_int(socket, SOL_SOCKET, SO_RCVBUF)?.max(0) as u32)
}
SocketOptionKind::SendBufferSize => {
SocketOption::SendBufferSize(get_int(socket, SOL_SOCKET, SO_SNDBUF)?.max(0) as u32)
}
SocketOptionKind::Ttl => {
let (level, name) = ip_level(socket, IP_TTL, IPV6_UNICAST_HOPS)?;
SocketOption::Ttl(get_int(socket, level, name)?.max(0) as u32)
}
SocketOptionKind::Ipv6Only => {
SocketOption::Ipv6Only(get_int(socket, IPPROTO_IPV6, IPV6_V6ONLY)? != 0)
}
SocketOptionKind::Broadcast => {
SocketOption::Broadcast(get_int(socket, SOL_SOCKET, SO_BROADCAST)? != 0)
}
SocketOptionKind::MulticastTtl => {
let (level, name) = ip_level(socket, IP_MULTICAST_TTL, IPV6_MULTICAST_HOPS)?;
SocketOption::MulticastTtl(get_int(socket, level, name)?.max(0) as u32)
}
SocketOptionKind::MulticastLoop => {
let (level, name) = ip_level(socket, IP_MULTICAST_LOOP, IPV6_MULTICAST_LOOP)?;
SocketOption::MulticastLoop(get_int(socket, level, name)? != 0)
}
})
}
pub(super) fn apply_accept_defaults(socket: usize, defaults: AcceptDefaults) -> Result<()> {
if defaults.nodelay {
set(socket, SocketOption::NoDelay(true))?;
}
if let Some(schedule) = defaults.keep_alive {
set(socket, SocketOption::KeepAlive(Some(schedule)))?;
}
Ok(())
}
pub(super) fn validate_accept_defaults(defaults: AcceptDefaults, kind: Kind) -> Result<()> {
if kind == Kind::Listener || defaults.is_empty() {
Ok(())
} else {
Err(Error::new(crate::ErrorKind::Unsupported))
}
}