blob: 69fbacf688b1b84118c14006577b571b31134395 [file] [log] [blame]
use std::io;
use std::mem;
use std::net::SocketAddr;
#[cfg(unix)]
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
#[cfg(windows)]
use std::os::windows::io::{AsRawSocket, FromRawSocket, IntoRawSocket, RawSocket};
use std::time::Duration;
use crate::net::{TcpListener, TcpStream};
use crate::sys;
/// A non-blocking TCP socket used to configure a stream or listener.
///
/// The `TcpSocket` type wraps the operating-system's socket handle. This type
/// is used to configure the socket before establishing a connection or start
/// listening for inbound connections.
///
/// The socket will be closed when the value is dropped.
#[derive(Debug)]
pub struct TcpSocket {
sys: sys::tcp::TcpSocket,
}
/// Configures a socket's TCP keepalive parameters.
#[derive(Debug, Default, Clone)]
pub struct TcpKeepalive {
pub(crate) time: Option<Duration>,
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "windows",
))]
pub(crate) interval: Option<Duration>,
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
))]
pub(crate) retries: Option<u32>,
}
impl TcpSocket {
/// Create a new IPv4 TCP socket.
///
/// This calls `socket(2)`.
pub fn new_v4() -> io::Result<TcpSocket> {
sys::tcp::new_v4_socket().map(|sys| TcpSocket { sys })
}
/// Create a new IPv6 TCP socket.
///
/// This calls `socket(2)`.
pub fn new_v6() -> io::Result<TcpSocket> {
sys::tcp::new_v6_socket().map(|sys| TcpSocket { sys })
}
pub(crate) fn new_for_addr(addr: SocketAddr) -> io::Result<TcpSocket> {
if addr.is_ipv4() {
TcpSocket::new_v4()
} else {
TcpSocket::new_v6()
}
}
/// Bind `addr` to the TCP socket.
pub fn bind(&self, addr: SocketAddr) -> io::Result<()> {
sys::tcp::bind(self.sys, addr)
}
/// Connect the socket to `addr`.
///
/// This consumes the socket and performs the connect operation. Once the
/// connection completes, the socket is now a non-blocking `TcpStream` and
/// can be used as such.
pub fn connect(self, addr: SocketAddr) -> io::Result<TcpStream> {
let stream = sys::tcp::connect(self.sys, addr)?;
// Don't close the socket
mem::forget(self);
Ok(TcpStream::from_std(stream))
}
/// Listen for inbound connections, converting the socket to a
/// `TcpListener`.
pub fn listen(self, backlog: u32) -> io::Result<TcpListener> {
let listener = sys::tcp::listen(self.sys, backlog)?;
// Don't close the socket
mem::forget(self);
Ok(TcpListener::from_std(listener))
}
/// Sets the value of `SO_REUSEADDR` on this socket.
pub fn set_reuseaddr(&self, reuseaddr: bool) -> io::Result<()> {
sys::tcp::set_reuseaddr(self.sys, reuseaddr)
}
/// Get the value of `SO_REUSEADDR` set on this socket.
pub fn get_reuseaddr(&self) -> io::Result<bool> {
sys::tcp::get_reuseaddr(self.sys)
}
/// Sets the value of `SO_REUSEPORT` on this socket.
/// Only supported available in unix
#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))]
pub fn set_reuseport(&self, reuseport: bool) -> io::Result<()> {
sys::tcp::set_reuseport(self.sys, reuseport)
}
/// Get the value of `SO_REUSEPORT` set on this socket.
/// Only supported available in unix
#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))]
pub fn get_reuseport(&self) -> io::Result<bool> {
sys::tcp::get_reuseport(self.sys)
}
/// Sets the value of `SO_LINGER` on this socket.
pub fn set_linger(&self, dur: Option<Duration>) -> io::Result<()> {
sys::tcp::set_linger(self.sys, dur)
}
/// Gets the value of `SO_LINGER` on this socket
pub fn get_linger(&self) -> io::Result<Option<Duration>> {
sys::tcp::get_linger(self.sys)
}
/// Sets the value of `SO_RCVBUF` on this socket.
pub fn set_recv_buffer_size(&self, size: u32) -> io::Result<()> {
sys::tcp::set_recv_buffer_size(self.sys, size)
}
/// Get the value of `SO_RCVBUF` set on this socket.
///
/// Note that if [`set_recv_buffer_size`] has been called on this socket
/// previously, the value returned by this function may not be the same as
/// the argument provided to `set_recv_buffer_size`. This is for the
/// following reasons:
///
/// * Most operating systems have minimum and maximum allowed sizes for the
/// receive buffer, and will clamp the provided value if it is below the
/// minimum or above the maximum. The minimum and maximum buffer sizes are
/// OS-dependent.
/// * Linux will double the buffer size to account for internal bookkeeping
/// data, and returns the doubled value from `getsockopt(2)`. As per `man
/// 7 socket`:
/// > Sets or gets the maximum socket receive buffer in bytes. The
/// > kernel doubles this value (to allow space for bookkeeping
/// > overhead) when it is set using `setsockopt(2)`, and this doubled
/// > value is returned by `getsockopt(2)`.
///
/// [`set_recv_buffer_size`]: #method.set_recv_buffer_size
pub fn get_recv_buffer_size(&self) -> io::Result<u32> {
sys::tcp::get_recv_buffer_size(self.sys)
}
/// Sets the value of `SO_SNDBUF` on this socket.
pub fn set_send_buffer_size(&self, size: u32) -> io::Result<()> {
sys::tcp::set_send_buffer_size(self.sys, size)
}
/// Get the value of `SO_SNDBUF` set on this socket.
///
/// Note that if [`set_send_buffer_size`] has been called on this socket
/// previously, the value returned by this function may not be the same as
/// the argument provided to `set_send_buffer_size`. This is for the
/// following reasons:
///
/// * Most operating systems have minimum and maximum allowed sizes for the
/// receive buffer, and will clamp the provided value if it is below the
/// minimum or above the maximum. The minimum and maximum buffer sizes are
/// OS-dependent.
/// * Linux will double the buffer size to account for internal bookkeeping
/// data, and returns the doubled value from `getsockopt(2)`. As per `man
/// 7 socket`:
/// > Sets or gets the maximum socket send buffer in bytes. The
/// > kernel doubles this value (to allow space for bookkeeping
/// > overhead) when it is set using `setsockopt(2)`, and this doubled
/// > value is returned by `getsockopt(2)`.
///
/// [`set_send_buffer_size`]: #method.set_send_buffer_size
pub fn get_send_buffer_size(&self) -> io::Result<u32> {
sys::tcp::get_send_buffer_size(self.sys)
}
/// Sets whether keepalive messages are enabled to be sent on this socket.
///
/// This will set the `SO_KEEPALIVE` option on this socket.
pub fn set_keepalive(&self, keepalive: bool) -> io::Result<()> {
sys::tcp::set_keepalive(self.sys, keepalive)
}
/// Returns whether or not TCP keepalive probes will be sent by this socket.
pub fn get_keepalive(&self) -> io::Result<bool> {
sys::tcp::get_keepalive(self.sys)
}
/// Sets parameters configuring TCP keepalive probes for this socket.
///
/// The supported parameters depend on the operating system, and are
/// configured using the [`TcpKeepalive`] struct. At a minimum, all systems
/// support configuring the [keepalive time]: the time after which the OS
/// will start sending keepalive messages on an idle connection.
///
/// # Notes
///
/// * This will enable TCP keepalive on this socket, if it is not already
/// enabled.
/// * On some platforms, such as Windows, any keepalive parameters *not*
/// configured by the `TcpKeepalive` struct passed to this function may be
/// overwritten with their default values. Therefore, this function should
/// either only be called once per socket, or the same parameters should
/// be passed every time it is called.
///
/// # Examples
#[cfg_attr(feature = "os-poll", doc = "```")]
#[cfg_attr(not(feature = "os-poll"), doc = "```ignore")]
/// use mio::net::{TcpSocket, TcpKeepalive};
/// use std::time::Duration;
///
/// # fn main() -> Result<(), std::io::Error> {
/// let socket = TcpSocket::new_v6()?;
/// let keepalive = TcpKeepalive::default()
/// .with_time(Duration::from_secs(4));
/// // Depending on the target operating system, we may also be able to
/// // configure the keepalive probe interval and/or the number of retries
/// // here as well.
///
/// socket.set_keepalive_params(keepalive)?;
/// # Ok(()) }
/// ```
///
/// [`TcpKeepalive`]: ../struct.TcpKeepalive.html
/// [keepalive time]: ../struct.TcpKeepalive.html#method.with_time
pub fn set_keepalive_params(&self, keepalive: TcpKeepalive) -> io::Result<()> {
self.set_keepalive(true)?;
sys::tcp::set_keepalive_params(self.sys, keepalive)
}
/// Returns the amount of time after which TCP keepalive probes will be sent
/// on idle connections.
///
/// If `None`, then keepalive messages are disabled.
///
/// This returns the value of `SO_KEEPALIVE` + `IPPROTO_TCP` on OpenBSD,
/// NetBSD, and Haiku, `TCP_KEEPALIVE` on macOS and iOS, and `TCP_KEEPIDLE`
/// on all other Unix operating systems. On Windows, it is not possible to
/// access the value of TCP keepalive parameters after they have been set.
///
/// Some platforms specify this value in seconds, so sub-second
/// specifications may be omitted.
#[cfg_attr(docsrs, doc(cfg(not(target_os = "windows"))))]
#[cfg(not(target_os = "windows"))]
pub fn get_keepalive_time(&self) -> io::Result<Option<Duration>> {
sys::tcp::get_keepalive_time(self.sys)
}
/// Returns the time interval between TCP keepalive probes, if TCP keepalive is
/// enabled on this socket.
///
/// If `None`, then keepalive messages are disabled.
///
/// This returns the value of `TCP_KEEPINTVL` on supported Unix operating
/// systems. On Windows, it is not possible to access the value of TCP
/// keepalive parameters after they have been set..
///
/// Some platforms specify this value in seconds, so sub-second
/// specifications may be omitted.
#[cfg_attr(
docsrs,
doc(cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
)))
)]
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
))]
pub fn get_keepalive_interval(&self) -> io::Result<Option<Duration>> {
sys::tcp::get_keepalive_interval(self.sys)
}
/// Returns the maximum number of TCP keepalive probes that will be sent before
/// dropping a connection, if TCP keepalive is enabled on this socket.
///
/// If `None`, then keepalive messages are disabled.
///
/// This returns the value of `TCP_KEEPCNT` on Unix operating systems that
/// support this option. On Windows, it is not possible to access the value
/// of TCP keepalive parameters after they have been set.
#[cfg_attr(
docsrs,
doc(cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
)))
)]
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
))]
pub fn get_keepalive_retries(&self) -> io::Result<Option<u32>> {
sys::tcp::get_keepalive_retries(self.sys)
}
/// Returns the local address of this socket
///
/// Will return `Err` result in windows if called before calling `bind`
pub fn get_localaddr(&self) -> io::Result<SocketAddr> {
sys::tcp::get_localaddr(self.sys)
}
}
impl Drop for TcpSocket {
fn drop(&mut self) {
sys::tcp::close(self.sys);
}
}
#[cfg(unix)]
impl IntoRawFd for TcpSocket {
fn into_raw_fd(self) -> RawFd {
let ret = self.sys;
// Avoid closing the socket
mem::forget(self);
ret
}
}
#[cfg(unix)]
impl AsRawFd for TcpSocket {
fn as_raw_fd(&self) -> RawFd {
self.sys
}
}
#[cfg(unix)]
impl FromRawFd for TcpSocket {
/// Converts a `RawFd` to a `TcpSocket`.
///
/// # Notes
///
/// The caller is responsible for ensuring that the socket is in
/// non-blocking mode.
unsafe fn from_raw_fd(fd: RawFd) -> TcpSocket {
TcpSocket { sys: fd }
}
}
#[cfg(windows)]
impl IntoRawSocket for TcpSocket {
fn into_raw_socket(self) -> RawSocket {
// The winapi crate defines `SOCKET` as `usize`. The Rust std
// conditionally defines `RawSocket` as a fixed size unsigned integer
// matching the pointer width. These end up being the same type but we
// must cast between them.
let ret = self.sys as RawSocket;
// Avoid closing the socket
mem::forget(self);
ret
}
}
#[cfg(windows)]
impl AsRawSocket for TcpSocket {
fn as_raw_socket(&self) -> RawSocket {
self.sys as RawSocket
}
}
#[cfg(windows)]
impl FromRawSocket for TcpSocket {
/// Converts a `RawSocket` to a `TcpSocket`.
///
/// # Notes
///
/// The caller is responsible for ensuring that the socket is in
/// non-blocking mode.
unsafe fn from_raw_socket(socket: RawSocket) -> TcpSocket {
TcpSocket {
sys: socket as sys::tcp::TcpSocket,
}
}
}
impl TcpKeepalive {
// Sets the amount of time after which TCP keepalive probes will be sent
/// on idle connections.
///
/// This will set the value of `SO_KEEPALIVE` + `IPPROTO_TCP` on OpenBSD,
/// NetBSD, and Haiku, `TCP_KEEPALIVE` on macOS and iOS, and `TCP_KEEPIDLE`
/// on all other Unix operating systems. On Windows, this sets the value of
/// the `tcp_keepalive` struct's `keepalivetime` field.
///
/// Some platforms specify this value in seconds, so sub-second
/// specifications may be omitted.
pub fn with_time(self, time: Duration) -> Self {
Self {
time: Some(time),
..self
}
}
/// Sets the time interval between TCP keepalive probes.
/// This sets the value of `TCP_KEEPINTVL` on supported Unix operating
/// systems. On Windows, this sets the value of the `tcp_keepalive` struct's
/// `keepaliveinterval` field.
///
/// Some platforms specify this value in seconds, so sub-second
/// specifications may be omitted.
#[cfg_attr(
docsrs,
doc(cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "windows"
)))
)]
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
target_os = "windows"
))]
pub fn with_interval(self, interval: Duration) -> Self {
Self {
interval: Some(interval),
..self
}
}
/// Sets the maximum number of TCP keepalive probes that will be sent before
/// dropping a connection, if TCP keepalive is enabled on this socket.
///
/// This will set the value of `TCP_KEEPCNT` on Unix operating systems that
/// support this option.
#[cfg_attr(
docsrs,
doc(cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
)))
)]
#[cfg(any(
target_os = "linux",
target_os = "macos",
target_os = "ios",
target_os = "freebsd",
target_os = "netbsd",
))]
pub fn with_retries(self, retries: u32) -> Self {
Self {
retries: Some(retries),
..self
}
}
/// Returns a new, empty set of TCP keepalive parameters.
pub fn new() -> Self {
Self::default()
}
}