use core::mem::ManuallyDrop;
use std::io;
use std::net::SocketAddr;
use std::time::Duration;
use super::opt;
use crate::io::raw::{Owned, Raw};
use crate::io::source::Kind;
use crate::io::{Handle, sys};
pub struct TcpSocket {
socket: Owned,
nonblocking: bool,
}
#[cfg(any(target_os = "linux", target_os = "android"))]
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct DeviceName {
bytes: [u8; libc::IFNAMSIZ],
len: u8,
}
#[cfg(any(target_os = "linux", target_os = "android"))]
impl core::ops::Deref for DeviceName {
type Target = [u8];
fn deref(&self) -> &[u8] {
&self.bytes[..usize::from(self.len)]
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
impl core::fmt::Debug for DeviceName {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
write!(f, "{:?}", String::from_utf8_lossy(self))
}
}
impl TcpSocket {
pub fn new_v4() -> io::Result<TcpSocket> {
Self::new(false)
}
pub fn new_v6() -> io::Result<TcpSocket> {
Self::new(true)
}
fn new(v6: bool) -> io::Result<TcpSocket> {
Ok(TcpSocket {
socket: super::tcp::new_socket(v6)?,
nonblocking: true,
})
}
fn raw(&self) -> Raw {
raw_of(&self.socket)
}
fn as_std(&self) -> ManuallyDrop<std::net::TcpStream> {
ManuallyDrop::new(unsafe { std_from_raw(self.raw()) })
}
pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> {
opt::set_flag(self.raw(), opt::SOL_SOCKET, opt::SO_KEEPALIVE, keepalive)
}
pub fn keepalive(&self) -> io::Result<bool> {
opt::flag(self.raw(), opt::SOL_SOCKET, opt::SO_KEEPALIVE)
}
pub fn set_reuseaddr(&self, reuseaddr: bool) -> io::Result<()> {
opt::set_flag(self.raw(), opt::SOL_SOCKET, opt::SO_REUSEADDR, reuseaddr)
}
pub fn reuseaddr(&self) -> io::Result<bool> {
opt::flag(self.raw(), opt::SOL_SOCKET, opt::SO_REUSEADDR)
}
#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))]
pub fn set_reuseport(&self, reuseport: bool) -> io::Result<()> {
opt::set_flag(self.raw(), opt::SOL_SOCKET, opt::SO_REUSEPORT, reuseport)
}
#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))]
pub fn reuseport(&self) -> io::Result<bool> {
opt::flag(self.raw(), opt::SOL_SOCKET, opt::SO_REUSEPORT)
}
pub fn set_send_buffer_size(&self, size: u32) -> io::Result<()> {
opt::set_size(self.raw(), opt::SOL_SOCKET, opt::SO_SNDBUF, size)
}
pub fn send_buffer_size(&self) -> io::Result<u32> {
opt::size(self.raw(), opt::SOL_SOCKET, opt::SO_SNDBUF)
}
pub fn set_recv_buffer_size(&self, size: u32) -> io::Result<()> {
opt::set_size(self.raw(), opt::SOL_SOCKET, opt::SO_RCVBUF, size)
}
pub fn recv_buffer_size(&self) -> io::Result<u32> {
opt::size(self.raw(), opt::SOL_SOCKET, opt::SO_RCVBUF)
}
pub fn set_linger(&self, linger: Option<Duration>) -> io::Result<()> {
opt::set_linger_time(self.raw(), linger)
}
pub fn set_zero_linger(&self) -> io::Result<()> {
opt::set_linger_time(self.raw(), Some(Duration::ZERO))
}
pub fn linger(&self) -> io::Result<Option<Duration>> {
opt::linger_time(self.raw())
}
pub fn set_nodelay(&self, nodelay: bool) -> io::Result<()> {
self.as_std().set_nodelay(nodelay)
}
pub fn nodelay(&self) -> io::Result<bool> {
self.as_std().nodelay()
}
pub fn set_tos(&self, tos: u32) -> io::Result<()> {
let tos = opt::Int::try_from(tos).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"the type of service is out of range",
)
})?;
opt::set(self.raw(), opt::IPPROTO_IP, opt::IP_TOS, tos)
}
pub fn tos(&self) -> io::Result<u32> {
opt::size(self.raw(), opt::IPPROTO_IP, opt::IP_TOS)
}
#[cfg(unix)]
pub fn set_tclass_v6(&self, tclass: u32) -> io::Result<()> {
let tclass = opt::Int::try_from(tclass).map_err(|_| {
io::Error::new(
io::ErrorKind::InvalidInput,
"the traffic class is out of range",
)
})?;
opt::set(self.raw(), opt::IPPROTO_IPV6, opt::IPV6_TCLASS, tclass)
}
#[cfg(unix)]
pub fn tclass_v6(&self) -> io::Result<u32> {
opt::size(self.raw(), opt::IPPROTO_IPV6, opt::IPV6_TCLASS)
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn bind_device(&self, interface: Option<&[u8]>) -> io::Result<()> {
let name = interface.unwrap_or_default();
unsafe {
opt::set_raw(
self.raw(),
opt::SOL_SOCKET,
libc::SO_BINDTODEVICE,
name.as_ptr(),
name.len(),
)
}
}
#[cfg(any(target_os = "linux", target_os = "android"))]
pub fn device(&self) -> io::Result<Option<DeviceName>> {
let mut name = DeviceName {
bytes: [0; libc::IFNAMSIZ],
len: 0,
};
let len = unsafe {
opt::get_raw(
self.raw(),
opt::SOL_SOCKET,
libc::SO_BINDTODEVICE,
name.bytes.as_mut_ptr(),
name.bytes.len(),
)?
};
let len = name.bytes[..len.min(name.bytes.len())]
.iter()
.position(|&b| b == 0)
.unwrap_or(len.min(name.bytes.len()));
name.len = len as u8;
Ok((len > 0).then_some(name))
}
pub fn local_addr(&self) -> io::Result<SocketAddr> {
self.as_std().local_addr()
}
pub fn take_error(&self) -> io::Result<Option<io::Error>> {
self.as_std().take_error()
}
pub fn bind(&self, addr: SocketAddr) -> io::Result<()> {
sys::bind_inet(self.raw(), &addr)
}
pub async fn connect(self, addr: SocketAddr) -> io::Result<super::TcpStream> {
let TcpSocket {
socket,
nonblocking,
} = self;
super::tcp::connect_socket(socket, nonblocking, addr).await
}
pub fn listen(self, backlog: u32) -> io::Result<super::TcpListener> {
sys::listen(self.raw(), backlog)?;
let listener = std::net::TcpListener::from(self.socket);
if self.nonblocking {
Ok(Handle::from_nonblocking(listener, Kind::TcpListener))
} else {
Handle::adopt(listener, Kind::TcpListener)
}
}
pub fn from_std_stream(stream: std::net::TcpStream) -> TcpSocket {
TcpSocket {
socket: Owned::from(stream),
nonblocking: false,
}
}
}
impl core::fmt::Debug for TcpSocket {
fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
f.debug_struct("TcpSocket")
.field("socket", &self.raw())
.finish_non_exhaustive()
}
}
#[cfg(unix)]
fn raw_of(socket: &Owned) -> Raw {
use std::os::fd::AsRawFd;
socket.as_raw_fd()
}
#[cfg(windows)]
fn raw_of(socket: &Owned) -> Raw {
use std::os::windows::io::AsRawSocket;
socket.as_raw_socket()
}
#[cfg(unix)]
unsafe fn std_from_raw(raw: Raw) -> std::net::TcpStream {
use std::os::fd::FromRawFd;
unsafe { std::net::TcpStream::from_raw_fd(raw) }
}
#[cfg(windows)]
unsafe fn std_from_raw(raw: Raw) -> std::net::TcpStream {
use std::os::windows::io::FromRawSocket;
unsafe { std::net::TcpStream::from_raw_socket(raw) }
}
#[cfg(unix)]
mod fd {
use std::os::fd::{AsFd, AsRawFd, BorrowedFd, FromRawFd, IntoRawFd, OwnedFd, RawFd};
use super::TcpSocket;
impl AsRawFd for TcpSocket {
fn as_raw_fd(&self) -> RawFd {
self.socket.as_raw_fd()
}
}
impl AsFd for TcpSocket {
fn as_fd(&self) -> BorrowedFd<'_> {
self.socket.as_fd()
}
}
impl FromRawFd for TcpSocket {
unsafe fn from_raw_fd(fd: RawFd) -> TcpSocket {
TcpSocket {
socket: unsafe { OwnedFd::from_raw_fd(fd) },
nonblocking: false,
}
}
}
impl IntoRawFd for TcpSocket {
fn into_raw_fd(self) -> RawFd {
self.socket.into_raw_fd()
}
}
}
#[cfg(windows)]
mod raw_socket {
use std::os::windows::io::{
AsRawSocket, AsSocket, BorrowedSocket, FromRawSocket, IntoRawSocket, OwnedSocket, RawSocket,
};
use super::TcpSocket;
impl AsRawSocket for TcpSocket {
fn as_raw_socket(&self) -> RawSocket {
self.socket.as_raw_socket()
}
}
impl AsSocket for TcpSocket {
fn as_socket(&self) -> BorrowedSocket<'_> {
self.socket.as_socket()
}
}
impl FromRawSocket for TcpSocket {
unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket {
TcpSocket {
socket: unsafe { OwnedSocket::from_raw_socket(socket) },
nonblocking: false,
}
}
}
impl IntoRawSocket for TcpSocket {
fn into_raw_socket(self) -> RawSocket {
self.socket.into_raw_socket()
}
}
}