use std::io;
use std::io::Error;
use std::net::SocketAddr;
use std::os::fd::{AsRawFd, FromRawFd, OwnedFd, RawFd};
use libc::{
F_GETFL, F_SETFD, F_SETFL, FD_CLOEXEC, O_NONBLOCK, SO_REUSEADDR, SO_REUSEPORT, SOL_SOCKET,
bind, c_int, c_void, fcntl, listen, setsockopt, socket, socklen_t,
};
use crate::driver::Driver;
use crate::io::socket::addr::Addr;
use crate::io::socket::{Domain, Kind, ListenerConfig};
use crate::platform::raw::abi::PlatformAbi;
#[derive(Debug)]
pub(crate) struct Handle {
fd: OwnedFd,
}
impl Handle {
#[must_use]
pub(crate) fn from_owned(fd: OwnedFd) -> Self {
Self { fd }
}
#[cfg(not(target_os = "linux"))]
pub(crate) fn into_owned(self) -> OwnedFd {
self.fd
}
fn check(rc: c_int) -> io::Result<()> {
if rc < 0 {
Err(Error::last_os_error())
} else {
Ok(())
}
}
pub(crate) fn set_cloexec(&self) -> io::Result<()> {
let rc = unsafe { fcntl(self.fd.as_raw_fd(), F_SETFD, FD_CLOEXEC) };
Self::check(rc)
}
pub(crate) fn set_nonblocking(&self) -> io::Result<()> {
let flags = unsafe { fcntl(self.fd.as_raw_fd(), F_GETFL, 0) };
if flags < 0 {
return Err(Error::last_os_error());
}
let rc = unsafe { fcntl(self.fd.as_raw_fd(), F_SETFL, flags | O_NONBLOCK) };
Self::check(rc)
}
pub(crate) fn open(domain: Domain, kind: Kind) -> io::Result<Self> {
let raw = unsafe { socket(domain.raw(), kind.raw(), 0) };
if raw < 0 {
return Err(Error::last_os_error());
}
let sock = Self::from_owned(unsafe { OwnedFd::from_raw_fd(raw) });
sock.set_cloexec()?;
Driver::set_no_sigpipe(&sock)?;
Ok(sock)
}
pub(crate) fn bind(&self, addr: &Addr) -> io::Result<()> {
let rc = unsafe { bind(self.as_raw_fd(), addr.ptr(), addr.socklen()) };
Self::check(rc)
}
pub(crate) fn listen(&self, backlog: i32) -> io::Result<()> {
let rc = unsafe { listen(self.as_raw_fd(), backlog) };
Self::check(rc)
}
pub(crate) fn setsockopt_raw(&self, level: c_int, opt: c_int, value: c_int) -> io::Result<()> {
let rc = unsafe {
setsockopt(
self.as_raw_fd(),
level,
opt,
&value as *const c_int as *const c_void,
size_of::<c_int>() as socklen_t,
)
};
Self::check(rc)
}
pub(crate) fn apply_reuse(&self, config: &ListenerConfig) -> io::Result<()> {
if config.reuse_addr {
self.setsockopt_raw(SOL_SOCKET, SO_REUSEADDR, 1)?;
}
if config.reuse_port {
self.setsockopt_raw(SOL_SOCKET, SO_REUSEPORT, 1)?;
}
Ok(())
}
pub(crate) fn local_addr(&self) -> io::Result<SocketAddr> {
Addr::from_getsockname(self.as_raw_fd())?.into_std()
}
}
impl AsRawFd for Handle {
fn as_raw_fd(&self) -> RawFd {
self.fd.as_raw_fd()
}
}