use super::poller::last_error;
use crate::{Error, ErrorKind, Result};
use std::{
mem::{size_of, zeroed},
net::{Ipv4Addr, Ipv6Addr, SocketAddr, SocketAddrV4, SocketAddrV6},
os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd},
};
pub(crate) struct Addr {
pub storage: libc::sockaddr_storage,
pub len: libc::socklen_t,
}
impl Addr {
pub fn new(addr: SocketAddr) -> Self {
let mut storage: libc::sockaddr_storage = unsafe { zeroed() };
let len;
match addr {
SocketAddr::V4(a) => {
let mut s: libc::sockaddr_in = unsafe { zeroed() };
s.sin_family = libc::AF_INET as _;
s.sin_port = a.port().to_be();
s.sin_addr.s_addr = u32::from_ne_bytes(a.ip().octets());
len = size_of::<libc::sockaddr_in>() as libc::socklen_t;
#[cfg(turnloop_backend = "kqueue")]
{
s.sin_len = len as u8;
}
unsafe {
std::ptr::write(
(&mut storage as *mut libc::sockaddr_storage).cast::<libc::sockaddr_in>(),
s,
);
}
}
SocketAddr::V6(a) => {
let mut s: libc::sockaddr_in6 = unsafe { zeroed() };
s.sin6_family = libc::AF_INET6 as _;
s.sin6_port = a.port().to_be();
s.sin6_flowinfo = a.flowinfo().to_be();
s.sin6_scope_id = a.scope_id();
s.sin6_addr.s6_addr = a.ip().octets();
len = size_of::<libc::sockaddr_in6>() as libc::socklen_t;
#[cfg(turnloop_backend = "kqueue")]
{
s.sin6_len = len as u8;
}
unsafe {
std::ptr::write(
(&mut storage as *mut libc::sockaddr_storage).cast::<libc::sockaddr_in6>(),
s,
);
}
}
}
Self { storage, len }
}
pub fn empty() -> Self {
Self {
storage: unsafe { zeroed() },
len: size_of::<libc::sockaddr_storage>() as _,
}
}
pub fn ptr(&self) -> *const libc::sockaddr {
(&self.storage as *const libc::sockaddr_storage).cast()
}
pub fn mut_ptr(&mut self) -> *mut libc::sockaddr {
(&mut self.storage as *mut libc::sockaddr_storage).cast()
}
pub fn decode(&self) -> Result<SocketAddr> {
match self.storage.ss_family as i32 {
libc::AF_INET if self.len as usize >= size_of::<libc::sockaddr_in>() => {
let s = unsafe { &*self.ptr().cast::<libc::sockaddr_in>() };
Ok(SocketAddr::V4(SocketAddrV4::new(
Ipv4Addr::from(s.sin_addr.s_addr.to_ne_bytes()),
u16::from_be(s.sin_port),
)))
}
libc::AF_INET6 if self.len as usize >= size_of::<libc::sockaddr_in6>() => {
let s = unsafe { &*self.ptr().cast::<libc::sockaddr_in6>() };
Ok(SocketAddr::V6(SocketAddrV6::new(
Ipv6Addr::from(s.sin6_addr.s6_addr),
u16::from_be(s.sin6_port),
u32::from_be(s.sin6_flowinfo),
s.sin6_scope_id,
)))
}
_ => Err(Error::new(ErrorKind::InvalidInput)),
}
}
}
pub(crate) fn option(fd: RawFd, level: i32, name: i32, value: i32) -> Result<()> {
if unsafe {
libc::setsockopt(
fd,
level,
name,
(&value as *const i32).cast(),
size_of::<i32>() as _,
)
} < 0
{
return Err(last_error());
}
Ok(())
}
pub(crate) fn configure(fd: RawFd) -> Result<()> {
let flags = unsafe { libc::fcntl(fd, libc::F_GETFL) };
if flags < 0 {
return Err(last_error());
}
if unsafe { libc::fcntl(fd, libc::F_SETFL, flags | libc::O_NONBLOCK) } < 0 {
return Err(last_error());
}
if unsafe { libc::fcntl(fd, libc::F_SETFD, libc::FD_CLOEXEC) } < 0 {
return Err(last_error());
}
#[cfg(turnloop_backend = "kqueue")]
option(fd, libc::SOL_SOCKET, libc::SO_NOSIGPIPE, 1)?;
Ok(())
}
pub(crate) fn create(addr: SocketAddr, udp: bool) -> Result<OwnedFd> {
let domain = if addr.is_ipv4() {
libc::AF_INET
} else {
libc::AF_INET6
};
let kind = if udp {
libc::SOCK_DGRAM
} else {
libc::SOCK_STREAM
};
#[cfg(turnloop_backend = "epoll")]
let kind = kind | libc::SOCK_CLOEXEC | libc::SOCK_NONBLOCK;
let fd = unsafe { libc::socket(domain, kind, 0) };
if fd < 0 {
return Err(last_error());
}
let fd = unsafe { OwnedFd::from_raw_fd(fd) };
configure(fd.as_raw_fd())?;
Ok(fd)
}
pub(crate) fn local_addr(fd: RawFd) -> Result<SocketAddr> {
let mut a = Addr::empty();
if unsafe { libc::getsockname(fd, a.mut_ptr(), &mut a.len) } < 0 {
return Err(last_error());
}
a.decode()
}
pub(crate) fn accept(fd: RawFd) -> Result<(OwnedFd, SocketAddr)> {
let mut a = Addr::empty();
let conn = unsafe { libc::accept(fd, a.mut_ptr(), &mut a.len) };
if conn < 0 {
return Err(last_error());
}
let conn = unsafe { OwnedFd::from_raw_fd(conn) };
configure(conn.as_raw_fd())?;
Ok((conn, a.decode()?))
}